This commit is contained in:
张洋川 2024-04-18 18:24:27 +08:00
commit 6181876f6c
65 changed files with 4069 additions and 1103 deletions

14
pom.xml
View File

@ -174,6 +174,17 @@
<version>${sf.version}</version>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-payment</artifactId>
<version>${sf.version}</version>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-service</artifactId>
<version>${sf.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.smarterFramework</groupId>
@ -214,6 +225,9 @@
<module>sf-common</module>
<module>sf-apijson</module>
<module>sf-oauth</module>
<module>sf-payment</module>
<module>sf-service</module>
<module>sf-order</module>
</modules>
<packaging>pom</packaging>

View File

@ -66,6 +66,21 @@
<artifactId>sf-oauth</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-order</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-payment</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-service</artifactId>
</dependency>
</dependencies>

View File

@ -7,127 +7,147 @@ import com.sf.common.core.domain.BaseEntity;
/**
* 应用列对象 APPLY_LIST_INFO
*
*
* @author ztzh
* @date 2024-04-11
*/
public class ApplyListInfo extends BaseEntity
{
public class ApplyListInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 主键 */
/**
* 主键
*/
private Long id;
/** 应用名称 */
/**
* 应用code
*/
@Excel(name = "应用编号")
private String appCode;
/**
* 应用名称
*/
@Excel(name = "应用名称")
private String appName;
/** 应用描述 */
/**
* 应用描述
*/
@Excel(name = "应用描述")
private String appDesc;
/** 图片 */
/**
* 图片
*/
@Excel(name = "图片")
private String picture;
/** 排序 */
/**
* 排序
*/
private Long orderNum;
/** 逻辑删除,0:未删除,1:删除 */
/**
* 逻辑删除,0:未删除,1:删除
*/
private Long isDelete;
/** 创建人 */
/**
* 创建人
*/
private String created;
/** 更新人 */
/**
* 更新人
*/
private String modified;
public void setId(Long id)
{
public void setId(Long id) {
this.id = id;
}
public Long getId()
{
public Long getId() {
return id;
}
public void setAppName(String appName)
{
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppName()
{
public String getAppName() {
return appName;
}
public void setAppDesc(String appDesc)
{
public void setAppDesc(String appDesc) {
this.appDesc = appDesc;
}
public String getAppDesc()
{
public String getAppDesc() {
return appDesc;
}
public void setPicture(String picture)
{
public void setPicture(String picture) {
this.picture = picture;
}
public String getPicture()
{
public String getPicture() {
return picture;
}
public void setOrderNum(Long orderNum)
{
public void setOrderNum(Long orderNum) {
this.orderNum = orderNum;
}
public Long getOrderNum()
{
public Long getOrderNum() {
return orderNum;
}
public void setIsDelete(Long isDelete)
{
public void setIsDelete(Long isDelete) {
this.isDelete = isDelete;
}
public Long getIsDelete()
{
public Long getIsDelete() {
return isDelete;
}
public void setCreated(String created)
{
public void setCreated(String created) {
this.created = created;
}
public String getCreated()
{
public String getCreated() {
return created;
}
public void setModified(String modified)
{
public void setModified(String modified) {
this.modified = modified;
}
public String getModified()
{
public String getModified() {
return modified;
}
public void setAppCode(String appCode) {
this.appCode = appCode;
}
public String getAppCode() {
return appCode;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("appName", getAppName())
.append("appDesc", getAppDesc())
.append("picture", getPicture())
.append("orderNum", getOrderNum())
.append("isDelete", getIsDelete())
.append("created", getCreated())
.append("modified", getModified())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.toString();
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("appCode",getAppCode())
.append("appName", getAppName())
.append("appDesc", getAppDesc())
.append("picture", getPicture())
.append("orderNum", getOrderNum())
.append("isDelete", getIsDelete())
.append("created", getCreated())
.append("modified", getModified())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.toString();
}
}

View File

@ -2,6 +2,7 @@ package com.sf.index.service.impl;
import java.util.List;
import com.sf.common.utils.DateUtils;
import com.sf.common.utils.uuid.IdUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sf.index.mapper.ApplyListInfoMapper;
@ -54,6 +55,7 @@ public class ApplyListInfoServiceImpl implements IApplyListInfoService
public int insertApplyListInfo(ApplyListInfo applyListInfo)
{
applyListInfo.setCreateTime(DateUtils.getNowDate());
applyListInfo.setAppCode(IdUtils.randomTime("ZT"));
return applyListInfoMapper.insertApplyListInfo(applyListInfo);
}

View File

@ -1,367 +0,0 @@
package com.sf.order.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.sf.common.annotation.Excel;
import com.sf.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
/**
* 订单基础信息对象 ORDER_INFO
*
* @author ztzh
* @date 2024-04-09
*/
public class OrderInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private Long id;
/**
* 订单编号
*/
@Excel(name = "订单编号")
private Long orderNo;
/**
* 订单状态:
* 0:待支付
* 1:已付款
* 2:支付超时系统结束
* 3:已完成
*/
@Excel(name = "订单状态: 0:待支付 1:已付款 2:支付超时系统结束 3:已完成 ")
private Long orderStatus;
/**
* 支付方式:0-点券 1-现金
*/
@Excel(name = "支付方式:0-点券 1-现金")
private Long payType;
/**
* 支付渠道(支付方式为现金时)
* 0:微信
* 1:支付宝
*/
@Excel(name = "支付渠道(支付方式为现金时) 0:微信 1:支付宝")
private Long payChannel;
/**
* 订单金额
*/
@Excel(name = "订单金额")
private Long orderAmt;
/**
* 运费
*/
@Excel(name = "运费")
private Long freightAmt;
/**
* 总金额
*/
@Excel(name = "总金额")
private Long payAmt;
/**
* 实际支付金额
*/
@Excel(name = "实际支付金额")
private Long reallyAmt;
/**
* 收件方式
* 0-自提
* 1-快递 2-配送
*/
@Excel(name = "收件方式0-自提 1-快递 2-配送")
private Long receiveType;
/**
* 商品Id
*/
@Excel(name = "商品Id")
private Long goodsId;
/**
* 商户Id
*/
@Excel(name = "商户Id")
private Long businessId;
/**
* 收货地址配置Id
*/
@Excel(name = "收货地址配置Id")
private Long receiveAddrId;
/**
* 支付时间
*/
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date payTime;
/**
* 订单创建人
*/
@Excel(name = "订单创建人")
private Long createUserId;
/**
* 最后一次更新操作人
*/
@Excel(name = "最后一次更新操作人")
private Long updateUserId;
/**
* 逻辑删除标识
* 0:未删除
* 1:已删除
*/
@Excel(name = "逻辑删除标识 0:未删除 1:已删除")
private Long isDelete;
/**
* 快递单号
*/
@Excel(name = "快递单号")
private String trackNo;
/**
* 订单类型0:自动1:手动
*/
@Excel(name = "订单类型0:自动1:手动")
private Long orderType;
/**
* 平台外部订单号
*/
@Excel(name = "平台外部订单号")
private Long outOrderNo;
/**
* 平台支付返回值
*/
@Excel(name = "平台支付返回值")
private String payData;
/**
* 减免金额(优惠券抵扣)
*/
@Excel(name = "减免金额(优惠券抵扣)")
private Long reductionAmout;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setOrderNo(Long orderNo) {
this.orderNo = orderNo;
}
public Long getOrderNo() {
return orderNo;
}
public void setOrderStatus(Long orderStatus) {
this.orderStatus = orderStatus;
}
public Long getOrderStatus() {
return orderStatus;
}
public void setPayType(Long payType) {
this.payType = payType;
}
public Long getPayType() {
return payType;
}
public void setPayChannel(Long payChannel) {
this.payChannel = payChannel;
}
public Long getPayChannel() {
return payChannel;
}
public void setOrderAmt(Long orderAmt) {
this.orderAmt = orderAmt;
}
public Long getOrderAmt() {
return orderAmt;
}
public void setFreightAmt(Long freightAmt) {
this.freightAmt = freightAmt;
}
public Long getFreightAmt() {
return freightAmt;
}
public void setPayAmt(Long payAmt) {
this.payAmt = payAmt;
}
public Long getPayAmt() {
return payAmt;
}
public void setReallyAmt(Long reallyAmt) {
this.reallyAmt = reallyAmt;
}
public Long getReallyAmt() {
return reallyAmt;
}
public void setReceiveType(Long receiveType) {
this.receiveType = receiveType;
}
public Long getReceiveType() {
return receiveType;
}
public void setGoodsId(Long goodsId) {
this.goodsId = goodsId;
}
public Long getGoodsId() {
return goodsId;
}
public void setBusinessId(Long businessId) {
this.businessId = businessId;
}
public Long getBusinessId() {
return businessId;
}
public void setReceiveAddrId(Long receiveAddrId) {
this.receiveAddrId = receiveAddrId;
}
public Long getReceiveAddrId() {
return receiveAddrId;
}
public void setPayTime(Date payTime) {
this.payTime = payTime;
}
public Date getPayTime() {
return payTime;
}
public void setCreateUserId(Long createUserId) {
this.createUserId = createUserId;
}
public Long getCreateUserId() {
return createUserId;
}
public void setUpdateUserId(Long updateUserId) {
this.updateUserId = updateUserId;
}
public Long getUpdateUserId() {
return updateUserId;
}
public void setIsDelete(Long isDelete) {
this.isDelete = isDelete;
}
public Long getIsDelete() {
return isDelete;
}
public void setTrackNo(String trackNo) {
this.trackNo = trackNo;
}
public String getTrackNo() {
return trackNo;
}
public void setOrderType(Long orderType) {
this.orderType = orderType;
}
public Long getOrderType() {
return orderType;
}
public void setOutOrderNo(Long outOrderNo) {
this.outOrderNo = outOrderNo;
}
public Long getOutOrderNo() {
return outOrderNo;
}
public void setPayData(String payData) {
this.payData = payData;
}
public String getPayData() {
return payData;
}
public void setReductionAmout(Long reductionAmout) {
this.reductionAmout = reductionAmout;
}
public Long getReductionAmout() {
return reductionAmout;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("orderNo", getOrderNo())
.append("orderStatus", getOrderStatus())
.append("payType", getPayType())
.append("payChannel", getPayChannel())
.append("orderAmt", getOrderAmt())
.append("freightAmt", getFreightAmt())
.append("payAmt", getPayAmt())
.append("reallyAmt", getReallyAmt())
.append("receiveType", getReceiveType())
.append("goodsId", getGoodsId())
.append("businessId", getBusinessId())
.append("receiveAddrId", getReceiveAddrId())
.append("createTime", getCreateTime())
.append("payTime", getPayTime())
.append("createUserId", getCreateUserId())
.append("updateUserId", getUpdateUserId())
.append("isDelete", getIsDelete())
.append("updateTime", getUpdateTime())
.append("trackNo", getTrackNo())
.append("orderType", getOrderType())
.append("outOrderNo", getOutOrderNo())
.append("payData", getPayData())
.append("reductionAmout", getReductionAmout())
.toString();
}
}

View File

@ -1,55 +0,0 @@
package com.sf.order.domain.res;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 活动信息详情
*
*/
@Data
@Schema(name = "OrderListResVo", description = "订单列表")
public class OrderListResVo {
/**
* 主键id
*/
private Long id;
/**
* 订单编号
*/
private String orderNo;
/**
* 订单状态:
* 0:待支付
* 1:已付款
* 2:支付超时系统结束
* 3:已完成
*/
private Long orderStatus;
/**
* 订单金额
*/
private Long orderAmt;
/**
* 商品标题
*/
private String productTitle;
/**
* 图片
*/
private String productPicture;
/**
* 商品规格
*/
private String goodsSpec;
/**
* 商品数量
*/
private Integer goodsCount = 1;
}

View File

@ -96,7 +96,6 @@ public class CommonController
return AjaxResult.error(e.getMessage());
}
}
/**
* 通用上传请求多个
*/

View File

@ -0,0 +1,117 @@
package com.sf.web.controller.system;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.sf.common.utils.SecurityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sf.common.annotation.Log;
import com.sf.common.core.controller.BaseController;
import com.sf.common.core.domain.AjaxResult;
import com.sf.common.enums.BusinessType;
import com.sf.system.domain.UserMember;
import com.sf.system.service.IUserMemberService;
import com.sf.common.utils.poi.ExcelUtil;
import com.sf.common.core.page.TableDataInfo;
/**
* 会员Controller
*
* @author ztzh
* @date 2024-04-16
*/
@RestController
@RequestMapping("/system/member")
public class UserMemberController extends BaseController
{
@Autowired
private IUserMemberService userMemberService;
/**
* 查询会员列表
*/
@PreAuthorize("@ss.hasPermi('system:member:list')")
@GetMapping("/list")
public TableDataInfo list(UserMember userMember)
{
startPage();
List<UserMember> list = userMemberService.selectUserMemberList(userMember);
return getDataTable(list);
}
/**
* 导出会员列表
*/
@PreAuthorize("@ss.hasPermi('system:member:export')")
@Log(title = "会员", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, UserMember userMember)
{
List<UserMember> list = userMemberService.selectUserMemberList(userMember);
ExcelUtil<UserMember> util = new ExcelUtil<UserMember>(UserMember.class);
util.exportExcel(response, list, "会员数据");
}
/**
* 获取会员详细信息
*/
@PreAuthorize("@ss.hasPermi('system:member:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
return success(userMemberService.selectUserMemberById(id));
}
/**
* 获取会员详细信息,通过用户id
* 当前用户
*/
@GetMapping(value = "/currentUser")
public AjaxResult getInfoByUser()
{
Long userId = SecurityUtils.getUserId();
return success(userMemberService.selectUserMemberByUserId(userId));
}
/**
* 新增会员
*/
@PreAuthorize("@ss.hasPermi('system:member:add')")
@Log(title = "会员", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody UserMember userMember)
{
return toAjax(userMemberService.insertUserMember(userMember));
}
/**
* 修改会员
*/
@PreAuthorize("@ss.hasPermi('system:member:edit')")
@Log(title = "会员", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody UserMember userMember)
{
return toAjax(userMemberService.updateUserMember(userMember));
}
/**
* 删除会员
*/
@PreAuthorize("@ss.hasPermi('system:member:remove')")
@Log(title = "会员", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(userMemberService.deleteUserMemberByIds(ids));
}
}

View File

@ -137,10 +137,10 @@ file:
defaultMaxSize: 52428800
access:
#key: iEJhVfZ8ColMeAnooVFx
key: UVtE4aBgbzCfYthfCV9P
key: CvproVoq7XiXDTK1RLFT
secret:
#key: SNTORcnq76xiaegoJ7Ap7Clldoq8HJUXqyn484o0
key: 5rOuC0FPPrD2qEj2Eux4sGKKDgAKQ6dEatfLJOpZ
key: dekJkBHBkO4HccNKsiqAC2mkeNxVKEWSRoll3yqp
bucket:
#name: ruoyi
name: sc-device-manage
name: sac

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSDCCAjACCQCvqL+z6dKTrjANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJD
TjERMA8GA1UECAwIc2hpY2h1YW4xEDAOBgNVBAcMB2NoZW5nZHUxEjAQBgNVBAoM
CXpob25ndGlhbjEKMAgGA1UECwwBQjESMBAGA1UEAwwJemhvbmd0aWFuMB4XDTI0
MDQxNzA2MjIxNloXDTI1MDQxNzA2MjIxNlowZjELMAkGA1UEBhMCQ04xETAPBgNV
BAgMCHNoaWNodWFuMRAwDgYDVQQHDAdjaGVuZ2R1MRIwEAYDVQQKDAl6aG9uZ3Rp
YW4xCjAIBgNVBAsMAUIxEjAQBgNVBAMMCXpob25ndGlhbjCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMIXQc0iVEuN/c8PnryNQYP3WbF6irqnjTbk/kCV
552p30ZmdeRDGcFKdkohQLKT06sbqlaBzhGFpnLlb5iz1dG1sGZbw064RFZRlgED
P7ROP891dvj9scPxfWC1Moy6/9LXBQLIshD7zTxzOemqxdyJ1jELianrWWm4XFKM
jvl7BGcfS/VIEafrJ5WGzuRMKtf6SY8aD5Olu0sN61tnSIgjWwZnKkv4V764UBpN
ybTsk1ctefUNMkE5Q+b2gu+0i5qH18B5/H5O7oAEAA+IWIPxIzuINnAYvHRhNQ/W
LMaI/GhYpQMhlmXtOmFFFe3fL4fKRMCKa3x2Va/GzfPjwecCAwEAATANBgkqhkiG
9w0BAQsFAAOCAQEAZmqK3ZfCA3A99VRfSRmCnpBbAW7SIxNeRqKFa5dunOMb7R6B
+bV/ALLJmu8S5D7pSR/wqCGURmnetUnBAEQmYJsjSq3142PeADkr8NmZ9bOXBVNM
szfSPOeM8HCUVjG9r+VMuy6yuPXFzp6QcKC13pzdziMdTHyKSu25fbYwE3lYOXk9
4nLpnJeT09gecXPdGllrVTKkgkE7L7h4iX/QzsGiZ/JbrCD77nPFqr7fkepsrHjl
4U1Pfjc3NStquhT5ZINDr7MBg/JaHNW57ynt1ud0y1lUOu2o5fpmyYVy/4llRl+T
VJ6mJYXIWr+i10bVjNv2tGPEylmI1JHelYRqAQ==
-----END CERTIFICATE-----

View File

@ -6,6 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<resultMap type="ApplyListInfo" id="ApplyListInfoResult">
<result property="id" column="id" />
<result property="appCode" column="app_code" />
<result property="appName" column="app_name" />
<result property="appDesc" column="app_desc" />
<result property="picture" column="picture" />
@ -18,7 +19,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectApplyListInfoVo">
select id, app_name, app_desc, picture, order_num, is_delete, created, modified, create_time, update_time from APPLY_LIST_INFO
select id, app_code, app_name, app_desc, picture, order_num, is_delete, created, modified, create_time, update_time from APPLY_LIST_INFO
</sql>
<select id="selectApplyListInfoList" parameterType="ApplyListInfo" resultMap="ApplyListInfoResult">
@ -36,6 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="insertApplyListInfo" parameterType="ApplyListInfo" useGeneratedKeys="true" keyProperty="id">
insert into APPLY_LIST_INFO
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="appCode != null and appCode != ''">app_code,</if>
<if test="appName != null and appName != ''">app_name,</if>
<if test="appDesc != null and appDesc != ''">app_desc,</if>
<if test="picture != null">picture,</if>
@ -47,6 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="appCode != null and appCode != ''">#{appCode},</if>
<if test="appName != null and appName != ''">#{appName},</if>
<if test="appDesc != null and appDesc != ''">#{appDesc},</if>
<if test="picture != null">#{picture},</if>
@ -62,6 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="updateApplyListInfo" parameterType="ApplyListInfo">
update APPLY_LIST_INFO
<trim prefix="SET" suffixOverrides=",">
<if test="appCode != null and appCode != ''">app_code = #{appCode},</if>
<if test="appName != null and appName != ''">app_name = #{appName},</if>
<if test="appDesc != null and appDesc != ''">app_desc = #{appDesc},</if>
<if test="picture != null">picture = #{picture},</if>

View File

@ -15,6 +15,12 @@
<dependencies>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>

View File

@ -6,25 +6,27 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import com.sf.common.config.SmarterFrameworkConfig;
import com.sf.common.exception.ServiceException;
import com.sf.common.utils.DateUtils;
import com.sf.common.utils.StringUtils;
import com.sf.common.utils.uuid.IdUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
/**
* 文件处理工具类
@ -295,5 +297,28 @@ public class FileUtils
String baseName = FilenameUtils.getBaseName(fileName);
return baseName;
}
/**
* 根据OutputStream生成文件
*/
public static MultipartFile outputToFile(ByteArrayOutputStream bos) throws IOException {
try {
// 文件输出流
byte[] bytes = bos.toByteArray();
FileItemFactory factory = new DiskFileItemFactory(16, null);
FileItem item = factory.createItem("file", "application/zip", false, "apk.zip");
OutputStream os = item.getOutputStream();
os.write(bytes);
bos.flush();
os.flush();
// MultipartFile
MultipartFile file = new CommonsMultipartFile(item);
return file;
} catch (Exception e) {
System.err.println("加载私钥失败");
}
return null;
}
}

View File

@ -0,0 +1,137 @@
package com.sf.common.utils.file;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* 压缩文件工具类
*/
public class ZipUtils {
private ZipFile zipFile;
private ZipOutputStream zipOut; //压缩Zip
private ZipEntry zipEntry;
private static int bufSize; //size of bytes
private byte[] buf;
private int readedBytes;
public ZipUtils() {
this(512);
}
public ZipUtils(int bufSize) {
this.bufSize = bufSize;
this.buf = new byte[this.bufSize];
}
//压缩文件夹内的文件
public void doZip(String zipDirectory) {//zipDirectoryPath:需要压缩的文件夹名
File file;
File zipDir;
zipDir = new File(zipDirectory);
String zipFileName = zipDir.getName() + ".zip";//压缩后生成的zip文件名
try {
this.zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFileName)));
handleDir(zipDir, this.zipOut);
this.zipOut.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
//由doZip调用,递归完成目录文件读取
public void handleDir(File dir, ZipOutputStream zipOut) throws IOException {
FileInputStream fileIn;
File[] files;
files = dir.listFiles();
if (files.length == 0) {//如果目录为空,则单独创建之.
//ZipEntry的isDirectory()方法中,目录以"/"结尾.
this.zipOut.putNextEntry(new ZipEntry(dir.toString() + "/"));
this.zipOut.closeEntry();
} else {//如果目录不为空,则分别处理目录和文件.
for (File fileName : files) {
//System.out.println(fileName);
if (fileName.isDirectory()) {
handleDir(fileName, this.zipOut);
} else {
fileIn = new FileInputStream(fileName);
this.zipOut.putNextEntry(new ZipEntry(fileName.toString()));
while ((this.readedBytes = fileIn.read(this.buf)) > 0) {
this.zipOut.write(this.buf, 0, this.readedBytes);
}
}
}
}
}
public static void zip(ZipOutputStream zipOut, InputStream inputStream, String fileName) throws IOException {
zipOut.putNextEntry(new ZipEntry(fileName));
int readBytes = 512;
byte[] buf = new byte[readBytes];
while ((readBytes = inputStream.read(buf)) > 0) {
zipOut.write(buf, 0, readBytes);
}
zipOut.closeEntry();
}
//解压指定zip文件
public void unZip(String unZipfileName) {//unZipfileName需要解压的zip文件名
FileOutputStream fileOut;
File file;
InputStream inputStream;
try {
this.zipFile = new ZipFile(unZipfileName);
for (Enumeration entries = this.zipFile.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement();
file = new File(entry.getName());
if (entry.isDirectory()) {
file.mkdirs();
} else {
//如果指定文件的目录不存在,则创建之.
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
inputStream = zipFile.getInputStream(entry);
fileOut = new FileOutputStream(file);
while ((this.readedBytes = inputStream.read(this.buf)) > 0) {
fileOut.write(this.buf, 0, this.readedBytes);
}
fileOut.close();
inputStream.close();
}
}
this.zipFile.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
//设置缓冲区大小
public void setBufSize(int bufSize) {
this.bufSize = bufSize;
}
}

View File

@ -0,0 +1,112 @@
package com.sf.common.utils.openssl;
import java.io.UnsupportedEncodingException;
/**
* Base64 加密解密工具类
* @author XIHONGLEI
* @date 2018-03-27
*/
public class Base64Utils {
private static char[] base64EncodeChars = new char[]
{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', '+', '/'};
private static byte[] base64DecodeChars = new byte[]
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1,
-1, -1, -1};
public static String encode(byte[] data) {
StringBuffer sb = new StringBuffer();
int len = data.length;
int i = 0;
int b1, b2, b3;
while (i < len) {
b1 = data[i++] & 0xff;
if (i == len) {
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[(b1 & 0x3) << 4]);
sb.append("==");
break;
}
b2 = data[i++] & 0xff;
if (i == len) {
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64EncodeChars[(b2 & 0x0f) << 2]);
sb.append("=");
break;
}
b3 = data[i++] & 0xff;
sb.append(base64EncodeChars[b1 >>> 2]);
sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
sb.append(base64EncodeChars[b3 & 0x3f]);
}
return sb.toString();
}
public static byte[] decode(String str) {
try {
return decodePrivate(str);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new byte[]
{};
}
private static byte[] decodePrivate(String str) throws UnsupportedEncodingException {
StringBuffer sb = new StringBuffer();
byte[] data = null;
data = str.getBytes("US-ASCII");
int len = data.length;
int i = 0;
int b1, b2, b3, b4;
while (i < len) {
do {
b1 = base64DecodeChars[data[i++]];
} while (i < len && b1 == -1);
if (b1 == -1) {
break;
}
do {
b2 = base64DecodeChars[data[i++]];
} while (i < len && b2 == -1);
if (b2 == -1) {
break;
}
sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4)));
do {
b3 = data[i++];
if (b3 == 61) {
return sb.toString().getBytes("iso8859-1");
}
b3 = base64DecodeChars[b3];
} while (i < len && b3 == -1);
if (b3 == -1) {
break;
}
sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));
do {
b4 = data[i++];
if (b4 == 61) {
return sb.toString().getBytes("iso8859-1");
}
b4 = base64DecodeChars[b4];
} while (i < len && b4 == -1);
if (b4 == -1) {
break;
}
sb.append((char) (((b3 & 0x03) << 6) | b4));
}
return sb.toString().getBytes("iso8859-1");
}
}

View File

@ -0,0 +1,72 @@
package com.sf.common.utils.openssl;
import java.io.*;
/**
* byte数组工具类实现byte[]与文件之间的相互转换
* @author XIHONGLEI
* @Date 2018-03-26
*/
public class ByteUtil {
/**
* 获得指定文件的byte数组
*/
public static byte[] getBytes(String filePath){
byte[] buffer = null;
try {
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
/**
* 根据byte数组生成文件
*/
public static void saveFile(byte[] bfile, String filePath,String fileName) {
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
try {
File dir = new File(filePath);
//判断文件目录是否存在
if(!dir.exists()&&dir.isDirectory()){
dir.mkdirs();
}
file = new File(filePath+"\\"+fileName);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bfile);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,447 @@
package com.sf.common.utils.openssl;
import cn.hutool.core.io.resource.ClassPathResource;
import com.sf.common.utils.file.FileUtils;
import com.sf.common.utils.file.ZipUtils;
import com.sf.common.utils.sign.Sha265Utils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.zip.ZipOutputStream;
/**
* rsa加密
*/
@Component
public class RsaSignUtil {
private static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqvm+B2e3shKKN1PGXfTWGc7+gC+XnKvtZX/T9ADWtL7KvAqhAREtxcFRtPC9ONbEqSfHb53mUUJbmKrp5C1rFrWQahBP8mpFOtjoReOkqF/idbwAKtvOq5DIuQT7Wl7wgDdh4OvEWqnUaWvkV0qJNK/Cny1YL8R+DJrnlAiREQwIDAQAB";
private static final String PRIVATE_KEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCF0HNIlRLjf3PD568jUGD91mxeoq6p4025P5Aleedqd9GZnXkQxnBSnZKIUCyk9OrG6pWgc4RhaZy5W+Ys9XRtbBmW8NOuERWUZYBAz+0Tj/PdXb4/bHD8X1gtTKMuv/S1wUCyLIQ+808cznpqsXcidYxC4mp61lpuFxSjI75ewRnH0v1SBGn6yeVhs7kTCrX+kmPGg+TpbtLDetbZ0iII1sGZypL+Fe+uFAaTcm07JNXLXn1DTJBOUPm9oLvtIuah9fAefx+Tu6ABAAPiFiD8SM7iDZwGLx0YTUP1izGiPxoWKUDIZZl7TphRRXt3y+HykTAimt8dlWvxs3z48HnAgMBAAECggEAMnoB7uuCqLOg0nzpd3f1FfD6LqID+k8g9qnPRdS2i9Zl7tUuy9J3nEFVSiojvU2cut9TCXRwtzXe9/qbb0c10HhEDBrJTGu3ugs1y8Wgh6/iAH44FsFDvgXL6Z62KstnRNoOv46aYLhbw+DjqtVy58whHFg30S0X79q2h/FuLYtQgju0I5yDMBVmbTmJKvhKtcATBUKnXxsAIMnc/BWXHNV9dg/Yfx1JDOqZqxFUsYmnMz5CatfGqnyc7AH1S0SFp4hIYsA1iPuEYDtFM8AAyP0PB4Z+buaG12xMF4S35OqDP0uhYpsh8/vQ28xqn0hAGsA1lAb792nZ5Qf/0WQGgQKBgQDuTNJdyIQN9Oa/DrQwDEPpyiurGBqY6ZvJHq1CpDy4AbMkIEYlVBnFPHCjyiWyW61MT80qMXyCQn3DLs5MsbB3rNZYItD9Tqmd5vkxD3B4zRneH1FQUuWR64lqM0fjFiy7+gfpn3tC5169ynn+u1oArwEnA577c93GHmr/P+I5FwKBgQDQgdCW8wTrLYLWrG7pg/VshWJX+J/8vGM8b2WPF52hIX7HhIouQjiwhAcMam0vwg0ZiPJH0Rw67PWrPxz1GOtfBwSXD74j/0QGdo/NwN8HxcXoGRFCb44KbDKj7r9xIwQ7eir93S7wb6FWzfzM4hcgQy6lZQO1wA1ld/n8GkOfsQKBgQDSZJUfJBgjUmQfr1W7TjjvwwWHC8L/2tEmYqVKP0M0mpuVe8Ey1+vgzhoNIj7iGDsLMzHK3OI3B4mNuFeAMynAn6KnMiYeX0M2HGVKwXzlyv/ZswDR0D7L9I7gauI/Rqa8WGaAB8SGL0zgeFH5a2RtUhXMYlANZiEZ+GUzcy3ChQKBgQCRtDV0Vox4X4xPwZ2b9we49xi7CtWaAneCUwHwVXtkjwiOGhOJseL1jr1yMqjsN7l5v5Y3E2ybzNz79xWgPPK1rCzL90dS1l3N+moRSRdR0iWzRFkoSNZEeEmG/x6uy954Ra3nSvozMH7ZLwbakZ/BftEI6ScHihaScp2xSxU4cQKBgQDANT+6d3E6aGT2oAdTxAdimcLEWZOVW9gkbX/UASEpTjEMDdr+MoPuKSPYxuMcWf/HksJF8rAVYJjklcIZHI1TF0Mm7RVrM+Kw3VzHR21jf7RNboBAAH+YeUH9yZg936iqO4Gi5wegUtttXgrxxG5rwi8iUXbbiC/xLVm0ldHW5Q==";
public static MultipartFile signZip(MultipartFile file) throws Exception {
RsaSignUtil rsaSignUtil = new RsaSignUtil();
//初始化密钥
try {
rsaSignUtil.loadPrivateKey(PRIVATE_KEY);
System.out.println("加载私钥成功");
} catch (Exception e) {
System.err.println(e.getMessage());
System.err.println("加载私钥失败");
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream out = new ZipOutputStream(outputStream);
//放入文件
ZipUtils.zip(out, new ByteArrayInputStream(file.getBytes()), file.getOriginalFilename());
//放入签名
String encrypt = Sha265Utils.encrypt(file.getBytes());
ZipUtils.zip(out, new ByteArrayInputStream(rsaSignUtil.rsaSign(encrypt.getBytes(), rsaSignUtil.getPrivateKey())), "sign.signture");
//放入证书
ZipUtils.zip(out, new ByteArrayInputStream(new ClassPathResource("file/server.crt").readBytes()), "server.crt");
outputStream.close();
out.close();
return FileUtils.outputToFile(outputStream);
}
/**
* rsa签名
* @param data 待签名的字符串
* @param priKey rsa私钥字符串
* @return 签名结果
* @throws Exception 签名失败则抛出异常
*/
public byte[] rsaSign(byte[] data, RSAPrivateKey priKey) throws SignatureException {
try {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(priKey);
signature.update(data);
byte[] signed = signature.sign();
return signed;
} catch (Exception e) {
throw new SignatureException("RSAcontent = " + data
+ "; charset = ", e);
}
}
/**
* rsa验签
*
* @param data 被签名的内容
* @param sign 签名后的结果
* @param pubKey rsa公钥
* @return 验签结果
* @throws SignatureException 验签失败则抛异常
*/
public boolean verify(byte[] data, byte[] sign, RSAPublicKey pubKey)
throws SignatureException {
try {
Signature signature = Signature.getInstance("SHA512withRSA");
signature.initVerify(pubKey);
signature.update(data);
return signature.verify(sign);
} catch (Exception e) {
e.printStackTrace();
throw new SignatureException("RSA验证签名[content = " + data
+ "; charset = " + "; signature = " + sign + "]发生异常!", e);
}
}
/**
* 私钥
*/
private RSAPrivateKey privateKey;
/**
* 公钥
*/
private RSAPublicKey publicKey;
/**
* 字节数据转字符串专用集合
*/
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 获取私钥
*
* @return 当前的私钥对象
*/
public RSAPrivateKey getPrivateKey() {
return privateKey;
}
/**
* 获取公钥
*
* @return 当前的公钥对象
*/
public RSAPublicKey getPublicKey() {
return publicKey;
}
/**
* 随机生成密钥对
*/
public void genKeyPair() {
KeyPairGenerator keyPairGen = null;
try {
keyPairGen = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keyPairGen.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGen.generateKeyPair();
this.privateKey = (RSAPrivateKey) keyPair.getPrivate();
this.publicKey = (RSAPublicKey) keyPair.getPublic();
}
/**
* .pem文件中取得私钥
*
* @param filePath 文件路径
* @return 私钥
*/
public String getPrivateKeyFromFile(String filePath) {
String strPrivateKey = "";
try {
BufferedReader privateKey = new BufferedReader(new FileReader(filePath));
String line = "";
while ((line = privateKey.readLine()) != null) {
strPrivateKey += line;
}
privateKey.close();
strPrivateKey = strPrivateKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "");
} catch (Exception e) {
e.printStackTrace();
}
return strPrivateKey;
}
/**
* .pem文件中取得公钥
*
* @param filePath 文件路径
* @return 公钥
*/
public String getPublicKeyFromFile(String filePath) {
String strPublicKey = "";
try {
BufferedReader publicKey = new BufferedReader(new FileReader(filePath));
String line = "";
while ((line = publicKey.readLine()) != null) {
strPublicKey += line;
}
publicKey.close();
strPublicKey = strPublicKey.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
} catch (Exception e) {
e.printStackTrace();
}
return strPublicKey;
}
/**
* 从字符串中加载公钥
*
* @param publicKeyStr 公钥数据字符串
* @throws Exception 加载公钥时产生的异常
*/
public void loadPublicKey(String publicKeyStr) throws Exception {
try {
byte[] buffer = Base64Utils.decode(publicKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
this.publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new Exception("无此算法");
} catch (InvalidKeySpecException e) {
throw new Exception("公钥非法");
} catch (NullPointerException e) {
throw new Exception("公钥数据为空");
}
}
/**
* 加载私钥
*
* @param privateKeyStr 私钥文件名
* @return 是否成功
* @throws Exception
*/
public void loadPrivateKey(String privateKeyStr) throws Exception {
try {
byte[] buffer = Base64Utils.decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new Exception("无此算法");
} catch (InvalidKeySpecException e) {
throw new Exception("私钥非法");
} catch (NullPointerException e) {
throw new Exception("私钥数据为空");
}
}
/**
* 加密过程
*
* @param publicKey 公钥
* @param plainTextData 明文数据
* @return
* @throws Exception 加密过程中的异常信息
*/
public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData)
throws Exception {
if (publicKey == null) {
throw new Exception("加密公钥为空, 请设置");
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] output = cipher.doFinal(plainTextData);
return output;
} catch (NoSuchAlgorithmException e) {
throw new Exception("无此加密算法");
} catch (NoSuchPaddingException e) {
e.printStackTrace();
return null;
} catch (InvalidKeyException e) {
throw new Exception("加密公钥非法,请检查");
} catch (IllegalBlockSizeException e) {
throw new Exception("明文长度非法");
} catch (BadPaddingException e) {
throw new Exception("明文数据已损坏");
}
}
/**
* 解密过程
*
* @param privateKey 私钥
* @param cipherData 密文数据
* @return 明文
* @throws Exception 解密过程中的异常信息
*/
public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData)
throws Exception {
if (privateKey == null) {
throw new Exception("解密私钥为空, 请设置");
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] output = cipher.doFinal(cipherData);
return output;
} catch (NoSuchAlgorithmException e) {
throw new Exception("无此解密算法");
} catch (NoSuchPaddingException e) {
e.printStackTrace();
return null;
} catch (InvalidKeyException e) {
throw new Exception("解密私钥非法,请检查");
} catch (IllegalBlockSizeException e) {
throw new Exception("密文长度非法");
} catch (BadPaddingException e) {
throw new Exception("密文数据已损坏");
}
}
/**
* 字节数据转十六进制字符串
*
* @param data 输入数据
* @return 十六进制内容
*/
public static String byteArrayToString(byte[] data) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < data.length; i++) {
// 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移
stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);
// 取出字节的低四位 作为索引得到相应的十六进制标识符
stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
if (i < data.length - 1) {
stringBuilder.append(' ');
}
}
return stringBuilder.toString();
}
/**
* btye转换hex函数
*
* @param byteArray
* @return
*/
public static String byteToHex(byte[] byteArray) {
StringBuffer strBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
strBuff.append("0").append(
Integer.toHexString(0xFF & byteArray[i]));
} else {
strBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
}
return strBuff.toString();
}
/**
* 以字节为单位读取文件常用于读二进制文件如图片声音影像等文件
*/
public static byte[] readFileByBytes(String fileName) {
File file = new File(fileName);
InputStream in = null;
byte[] txt = new byte[(int) file.length()];
try {
// 一次读一个字节
in = new FileInputStream(file);
int tempbyte;
int i = 0;
while ((tempbyte = in.read()) != -1) {
txt[i] = (byte) tempbyte;
i++;
}
in.close();
return txt;
} catch (IOException e) {
e.printStackTrace();
return txt;
}
}
// /**
// * Main 测试方法
// * @param args
// */
// public static void main(String[] args) throws FileNotFoundException, CertificateException {
// RsaEncrypt rsaEncrypt = new RsaEncrypt();
// try {
// String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhdBzSJUS439zw+evI1Bg/dZsXqKuqeNNuT+QJXnnanfRmZ15EMZwUp2SiFAspPTqxuqVoHOEYWmcuVvmLPV0bWwZlvDTrhEVlGWAQM/tE4/z3V2+P2xw/F9YLUyjLr/0tcFAsiyEPvNPHM56arF3InWMQuJqetZabhcUoyO+XsEZx9L9UgRp+snlYbO5Ewq1/pJjxoPk6W7Sw3rW2dIiCNbBmcqS/hXvrhQGk3JtOyTVy159Q0yQTlD5vaC77SLmofXwHn8fk7ugAQAD4hYg/EjO4g2cBi8dGE1D9Ysxoj8aFilAyGWZe06YUUV7d8vh8pEwIprfHZVr8bN8+PB5wIDAQAB";
// rsaEncrypt.loadPublicKey(publicKey);
// System.out.println("加载公钥成功");
// } catch (Exception e) {
// System.err.println(e.getMessage());
// System.err.println("加载公钥失败");
// }
//
// // 加载私钥
// try {
// String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCF0HNIlRLjf3P\n" +
// "D568jUGD91mxeoq6p4025P5Aleedqd9GZnXkQxnBSnZKIUCyk9OrG6pWgc4RhaZy\n" +
// "5W+Ys9XRtbBmW8NOuERWUZYBAz+0Tj/PdXb4/bHD8X1gtTKMuv/S1wUCyLIQ+808\n" +
// "cznpqsXcidYxC4mp61lpuFxSjI75ewRnH0v1SBGn6yeVhs7kTCrX+kmPGg+TpbtL\n" +
// "DetbZ0iII1sGZypL+Fe+uFAaTcm07JNXLXn1DTJBOUPm9oLvtIuah9fAefx+Tu6A\n" +
// "BAAPiFiD8SM7iDZwGLx0YTUP1izGiPxoWKUDIZZl7TphRRXt3y+HykTAimt8dlWv\n" +
// "xs3z48HnAgMBAAECggEAMnoB7uuCqLOg0nzpd3f1FfD6LqID+k8g9qnPRdS2i9Zl\n" +
// "7tUuy9J3nEFVSiojvU2cut9TCXRwtzXe9/qbb0c10HhEDBrJTGu3ugs1y8Wgh6/i\n" +
// "AH44FsFDvgXL6Z62KstnRNoOv46aYLhbw+DjqtVy58whHFg30S0X79q2h/FuLYtQ\n" +
// "gju0I5yDMBVmbTmJKvhKtcATBUKnXxsAIMnc/BWXHNV9dg/Yfx1JDOqZqxFUsYmn\n" +
// "Mz5CatfGqnyc7AH1S0SFp4hIYsA1iPuEYDtFM8AAyP0PB4Z+buaG12xMF4S35OqD\n" +
// "P0uhYpsh8/vQ28xqn0hAGsA1lAb792nZ5Qf/0WQGgQKBgQDuTNJdyIQN9Oa/DrQw\n" +
// "DEPpyiurGBqY6ZvJHq1CpDy4AbMkIEYlVBnFPHCjyiWyW61MT80qMXyCQn3DLs5M\n" +
// "sbB3rNZYItD9Tqmd5vkxD3B4zRneH1FQUuWR64lqM0fjFiy7+gfpn3tC5169ynn+\n" +
// "u1oArwEnA577c93GHmr/P+I5FwKBgQDQgdCW8wTrLYLWrG7pg/VshWJX+J/8vGM8\n" +
// "b2WPF52hIX7HhIouQjiwhAcMam0vwg0ZiPJH0Rw67PWrPxz1GOtfBwSXD74j/0QG\n" +
// "do/NwN8HxcXoGRFCb44KbDKj7r9xIwQ7eir93S7wb6FWzfzM4hcgQy6lZQO1wA1l\n" +
// "d/n8GkOfsQKBgQDSZJUfJBgjUmQfr1W7TjjvwwWHC8L/2tEmYqVKP0M0mpuVe8Ey\n" +
// "1+vgzhoNIj7iGDsLMzHK3OI3B4mNuFeAMynAn6KnMiYeX0M2HGVKwXzlyv/ZswDR\n" +
// "0D7L9I7gauI/Rqa8WGaAB8SGL0zgeFH5a2RtUhXMYlANZiEZ+GUzcy3ChQKBgQCR\n" +
// "tDV0Vox4X4xPwZ2b9we49xi7CtWaAneCUwHwVXtkjwiOGhOJseL1jr1yMqjsN7l5\n" +
// "v5Y3E2ybzNz79xWgPPK1rCzL90dS1l3N+moRSRdR0iWzRFkoSNZEeEmG/x6uy954\n" +
// "Ra3nSvozMH7ZLwbakZ/BftEI6ScHihaScp2xSxU4cQKBgQDANT+6d3E6aGT2oAdT\n" +
// "xAdimcLEWZOVW9gkbX/UASEpTjEMDdr+MoPuKSPYxuMcWf/HksJF8rAVYJjklcIZ\n" +
// "HI1TF0Mm7RVrM+Kw3VzHR21jf7RNboBAAH+YeUH9yZg936iqO4Gi5wegUtttXgrx\n" +
// "xG5rwi8iUXbbiC/xLVm0ldHW5Q==";
// rsaEncrypt.loadPrivateKey(privateKey);
// System.out.println("加载私钥成功");
// } catch (Exception e) {
// System.err.println(e.getMessage());
// System.err.println("加载私钥失败");
// }
// //测试字符串
// String encryptStr = "12321dsfasf1321312fsfdsafsdafasfsadf";
// try {
// System.out.println(new Date());
// System.out.println(new Date());
// // 加密
// byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(),
// encryptStr.getBytes());
// // 解密
// byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(),
// cipher);
// System.out.println(new Date());
// System.out.println(new String(plainText));
// byte[] content = readFileByBytes("/Users/a1234/Documents/ca/demo.zip");
// // 签名验证
// byte[] signbyte = rsaEncrypt.rsaSign(content, rsaEncrypt.getPrivateKey());
// System.out.println("签名-----" + byteToHex(signbyte));
// ByteUtil.saveFile(signbyte,"/Users/a1234/Documents/ca/","demo.zip.sign");
// Boolean isok = rsaEncrypt.verify(content, signbyte, rsaEncrypt.getPublicKey());
// System.out.println("验证:" + isok);
//
// // 读取验证文件
// byte[] read = readFileByBytes("/Users/a1234/Documents/ca/demo.zip.sign");
// System.out.println("读取签名文件:" + byteToHex(signbyte));
// Boolean isfok = rsaEncrypt.verify(content, read, rsaEncrypt.getPublicKey());
// System.out.println("文件验证2" + isfok);
//
// } catch (Exception e) {
// System.err.println(e.getMessage());
// }
// }
}

View File

@ -0,0 +1,26 @@
package com.sf.common.utils.sign;
import java.security.MessageDigest;
/**
* Sha256加密方法
*
* @author ztzh
*/
public class Sha265Utils
{
private static final String SHA_256_ALGORITHM = "SHA-256";
public static String encrypt(byte[] data) throws Exception {
//获取SHA-256算法实例
MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM);
//计算散列值
byte[] digest = messageDigest.digest(data);
StringBuilder stringBuilder = new StringBuilder();
//将byte数组转换为15进制字符串
for (byte b : digest) {
stringBuilder.append(Integer.toHexString((b & 0xFF) | 0x100).toUpperCase(), 1, 3);
}
return stringBuilder.toString();
}
}

View File

@ -2,48 +2,53 @@ package com.sf.common.utils.uuid;
/**
* ID生成器工具类
*
*
* @author ztzh
*/
public class IdUtils
{
public class IdUtils {
/**
* 获取随机UUID
*
*
* @return 随机UUID
*/
public static String randomUUID()
{
public static String randomUUID() {
return UUID.randomUUID().toString();
}
/**
* 简化的UUID去掉了横线
*
*
* @return 简化的UUID去掉了横线
*/
public static String simpleUUID()
{
public static String simpleUUID() {
return UUID.randomUUID().toString(true);
}
/**
* 获取随机UUID使用性能更好的ThreadLocalRandom生成UUID
*
*
* @return 随机UUID
*/
public static String fastUUID()
{
public static String fastUUID() {
return UUID.fastUUID().toString();
}
/**
* 简化的UUID去掉了横线使用性能更好的ThreadLocalRandom生成UUID
*
*
* @return 简化的UUID去掉了横线
*/
public static String fastSimpleUUID()
{
public static String fastSimpleUUID() {
return UUID.fastUUID().toString(true);
}
/**
* 生成一个由时间错与字母组成的唯一id
*
* @return
*/
public static String randomTime(String data) {
long uniqueID = System.currentTimeMillis();
return data + "_" + uniqueID;
}
}

View File

@ -124,7 +124,7 @@ public class SysOssController extends BaseController
*
* @param file 文件
*/
//@PreAuthorize("@ss.hasPermi('system:oss:upload')")
@PreAuthorize("@ss.hasPermi('system:oss:upload')")
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysOss> upload(@RequestPart("file") MultipartFile file) {
@ -136,6 +136,23 @@ public class SysOssController extends BaseController
throw new FileSizeLimitExceededException(defaultMaxSize / 1024 / 1024);
}
SysOss oss = sysOssService.upload(file);
oss.setSize(file.getSize());
return R.ok(oss);
}
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@PostMapping(value = "/zip/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysOss> zipUpload(@RequestPart("file") MultipartFile file) throws Exception {
if (ObjectUtil.isNull(file)) {
throw new ServiceException("上传文件不能为空");
}
long size = file.getSize();
if (size > defaultMaxSize) {
throw new FileSizeLimitExceededException(defaultMaxSize / 1024 / 1024);
}
SysOss oss = sysOssService.uploadSignZip(file);
oss.setSize(file.getSize());
return R.ok(oss);
}

View File

@ -47,6 +47,10 @@ public class SysOss extends BaseEntity
@Excel(name = "服务商")
private String service;
/** 文件大小 */
@Excel(name = "文件大小")
private Long size;
public void setId(String id)
{
this.id = id;
@ -119,6 +123,13 @@ public class SysOss extends BaseEntity
{
return service;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
@Override
public String toString() {
@ -135,6 +146,7 @@ public class SysOss extends BaseEntity
.append("updateTime", getUpdateTime())
.append("updateBy", getUpdateBy())
.append("service", getService())
.append("size",getSize())
.toString();
}
}

View File

@ -1,6 +1,8 @@
package com.sf.file.service;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
@ -69,4 +71,6 @@ public interface ISysOssService {
SysOss upload(File file);
void download(String ossId, HttpServletResponse response) throws Exception;
SysOss uploadSignZip(MultipartFile file) throws Exception;
}

View File

@ -1,13 +1,12 @@
package com.sf.file.service.impl;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.*;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import com.sf.common.utils.openssl.RsaSignUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -162,6 +161,10 @@ public class SysOssServiceImpl implements ISysOssService {
throw new ServiceException(e.getMessage());
}
}
@Override
public SysOss uploadSignZip(MultipartFile file) throws Exception {
return upload(RsaSignUtil.signZip(file));
}
public UploadResult uploadSuffix(File file, String suffix) {
return upload(file, getPath(null, suffix));

View File

@ -72,6 +72,18 @@
<groupId>com.smarterFramework</groupId>
<artifactId>sf-common</artifactId>
</dependency>
<!-- swagger3-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
</dependencies>
</project>

33
sf-order/pom.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>smarterFramework</artifactId>
<groupId>com.smarterFramework</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sf-order</artifactId>
<description>
订单模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-common</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-framework</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -3,17 +3,14 @@ package com.sf.order.controller;
import com.sf.common.annotation.Log;
import com.sf.common.core.controller.BaseController;
import com.sf.common.core.domain.AjaxResult;
import com.sf.common.core.domain.entity.SysUser;
import com.sf.common.core.page.TableDataInfo;
import com.sf.common.enums.BusinessType;
import com.sf.common.utils.SecurityUtils;
import com.sf.common.utils.poi.ExcelUtil;
import com.sf.order.domain.OrderInfo;
import com.sf.order.domain.dto.OrderCreateDto;
import com.sf.order.domain.req.OrderListReqVo;
import com.sf.order.domain.res.OrderListResVo;
import com.sf.order.service.IOrderInfoService;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -39,10 +36,7 @@ public class OrderInfoController extends BaseController {
@GetMapping("/list")
public TableDataInfo list(OrderListReqVo vo) {
startPage();
SysUser user = SecurityUtils.getLoginUser().getUser();
vo.setUserId(user.getUserId());
List<OrderListResVo> list = orderInfoService.queryList(vo);
logger.info("prderList"+list);
return getDataTable(list);
}
@ -72,7 +66,7 @@ public class OrderInfoController extends BaseController {
@Log(title = "创建订单基础信息", businessType = BusinessType.INSERT)
@PostMapping(value = "/createOrder")
public AjaxResult createOrder(@RequestBody OrderCreateDto orderCreateDto) {
return toAjax(orderInfoService.createOrder(orderCreateDto));
return AjaxResult.success(orderInfoService.createOrder(orderCreateDto));
}
/**
@ -87,7 +81,6 @@ public class OrderInfoController extends BaseController {
/**
* 支付订单
*/
@Operation(summary = "支付订单")
@PostMapping(value = "/pay/{orderId}")
private String orderPay(@PathVariable(value = "orderId") Long orderId) {
orderInfoService.orderPay(orderId);

View File

@ -0,0 +1,183 @@
package com.sf.order.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.sf.common.annotation.Excel;
import com.sf.common.core.domain.BaseEntity;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
/**
* 订单基础信息对象 ORDER_INFO
*
* @author ztzh
* @date 2024-04-09
*/
@Data
public class OrderInfo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private Long id;
/**
* 订单编号
*/
@Excel(name = "订单编号")
private Long orderNo;
/**
* 订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成
*/
@Excel(name = "订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成 ")
private Long orderStatus;
/**
* 支付方式:0-点券 1-现金
*/
@Excel(name = "支付方式:0-点券 1-现金")
private Long payType;
/**
* 支付渠道(支付方式为现金时)
* 0:微信
* 1:支付宝
*/
@Excel(name = "支付渠道(支付方式为现金时) 0:微信 1:支付宝")
private Long payChannel;
/**
* 订单金额
*/
@Excel(name = "订单金额")
private Long orderAmt;
/**
* 运费
*/
@Excel(name = "运费")
private Long freightAmt;
/**
* 总金额
*/
@Excel(name = "总金额")
private Long payAmt;
/**
* 实际支付金额
*/
@Excel(name = "实际支付金额")
private Long reallyAmt;
/**
* 收件方式
* 0-自提
* 1-快递 2-配送
*/
@Excel(name = "收件方式0-自提 1-快递 2-配送")
private Long receiveType;
/**
* 商品Id
*/
@Excel(name = "商品Id")
private Long goodsId;
/**
* 商户Id
*/
@Excel(name = "商户Id")
private Long businessId;
/**
* 收货地址配置Id
*/
@Excel(name = "收货地址配置Id")
private Long receiveAddrId;
/**
* 支付时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "支付时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date payTime;
/**
* 订单创建人
*/
@Excel(name = "订单创建人")
private Long createUserId;
/**
* 最后一次更新操作人
*/
@Excel(name = "最后一次更新操作人")
private Long updateUserId;
/**
* 逻辑删除标识
* 0:未删除
* 1:已删除
*/
@Excel(name = "逻辑删除标识 0:未删除 1:已删除")
private Long isDelete;
/**
* 快递单号
*/
@Excel(name = "快递单号")
private String trackNo;
/**
* 订单类型0:自动1:手动
*/
@Excel(name = "订单类型0:自动1:手动")
private Long orderType;
/**
* 平台外部订单号
*/
@Excel(name = "平台外部订单号")
private String outOrderNo;
/**
* 平台支付返回值
*/
@Excel(name = "平台支付返回值")
private String payData;
/**
* 减免金额(优惠券抵扣)
*/
@Excel(name = "减免金额(优惠券抵扣)")
private Long reductionAmout;
/**
* * 商品数量
*/
private Integer goodsCount;
/**
* * 商品类型
* * 0消耗型商品
* * 1非消耗型商品
* * 2自动续期订阅商品
*/
private Integer goodsType;
/**
* 商品单价
*/
private Long goodsPrice;
/**
* 商品编号
*/
private String goodsCode;
}

View File

@ -1,5 +1,6 @@
package com.sf.order.domain.dto;
import com.sf.common.annotation.Excel;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.NotNull;
@ -15,12 +16,13 @@ public class OrderCreateDto {
@NotNull(message = "商品id不能为空")
private Long goodsId;
@Schema(description = "平台外部订单号")
private String outOrderNo;
@Schema(description = "数量")
@NotNull(message = "数量不能唯恐")
private Long count;
@Schema(description = "金额")
@NotNull(message = "金额不能为空")
private Long amount;
@Schema(description = "用户id")
@ -56,4 +58,12 @@ public class OrderCreateDto {
public void setUserId(Long userId) {
this.userId = userId;
}
public String getOutOrderNo() {
return outOrderNo;
}
public void setOutOrderNo(String outOrderNo) {
this.outOrderNo = outOrderNo;
}
}

View File

@ -14,7 +14,7 @@ public class OrderListReqVo extends BaseEntity {
private Long userId;
/**
* 订单类型
* 订单状态
*/
private Long orderStatus;

View File

@ -0,0 +1,93 @@
package com.sf.order.domain.res;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.sf.common.annotation.Excel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* 活动信息详情
*
*/
@Data
@Schema(name = "OrderListResVo", description = "订单列表")
public class OrderListResVo {
/**
* 主键id
*/
private Long id;
/**
* 订单编号
*/
private String orderNo;
/**
* 订单状态: 0:待支付 1:已付款待发货 2:配送中 3:待取货 4:支付超时系统结束 5:客户自主取消 6:已完成
*/
private Long orderStatus;
/**
* 订单金额
*/
private Long orderAmt;
/**
* 支付时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date payTime;
/**
* 订阅订单取消时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date subscriptionCancellationTime;
/**
* 商品标题
*/
private String productTitle;
/**
* 图片
*/
private String productPicture;
/**
* 商品描述
*/
private String productDesc;
/**
* 商品规格
*/
private String goodsSpec;
/**
* 商品数量
*/
private Integer goodsCount;
/**
* 商品价格
*/
private Long goodsPrice;
/**
* * 商品类型
* * 0消耗型商品
* * 1非消耗型商品
* * 2自动续期订阅商品
*/
private Integer goodsType;
/**
* 商品编码
*/
private String goodsCode;
}

View File

@ -64,4 +64,6 @@ public interface OrderInfoMapper {
* @return 结果
*/
public int deleteOrderInfoByIds(Long[] ids);
OrderInfo selectOrderInfoByOrderNo(String orderNo);
}

View File

@ -43,7 +43,7 @@ public interface IOrderInfoService
* @param orderInfo 订单基础信息
* @return 结果
*/
public int createOrder(OrderCreateDto orderInfo);
public Long createOrder(OrderCreateDto orderInfo);
/**
* 修改订单基础信息
@ -70,4 +70,9 @@ public interface IOrderInfoService
public int deleteOrderInfoById(Long id);
void orderPay(Long orderId);
OrderInfo selectOrderInfoByOrderNo(String orderNo);
void insertOrder(OrderInfo orderInfo);
}

View File

@ -21,8 +21,7 @@ import java.util.List;
* @date 2024-04-09
*/
@Service
public class OrderInfoServiceImpl implements IOrderInfoService
{
public class OrderInfoServiceImpl implements IOrderInfoService {
@Resource
private OrderInfoMapper orderInfoMapper;
@Resource
@ -35,8 +34,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
* @return 订单基础信息
*/
@Override
public OrderInfo selectOrderInfoById(Long id)
{
public OrderInfo selectOrderInfoById(Long id) {
return orderInfoMapper.selectOrderInfoById(id);
}
@ -47,12 +45,11 @@ public class OrderInfoServiceImpl implements IOrderInfoService
* @return 订单基础信息
*/
@Override
public List<OrderInfo> selectOrderInfoList(OrderInfo orderInfo)
{
public List<OrderInfo> selectOrderInfoList(OrderInfo orderInfo) {
return orderInfoMapper.selectOrderInfoList(orderInfo);
}
public List<OrderListResVo> queryList(OrderListReqVo vo)
{
public List<OrderListResVo> queryList(OrderListReqVo vo) {
return orderInfoMapper.queryList(vo);
}
@ -63,11 +60,10 @@ public class OrderInfoServiceImpl implements IOrderInfoService
* @return 结果
*/
@Override
public int createOrder(OrderCreateDto orderCreateDto)
{
public Long createOrder(OrderCreateDto orderCreateDto) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderNo(snowflakeIdWorker.nextId());
orderInfo.setPayType(0L);
orderInfo.setPayType(1L);
orderInfo.setReceiveType(0L);
orderInfo.setOrderStatus(0L);
orderInfo.setCreateUserId(orderCreateDto.getUserId());
@ -77,7 +73,9 @@ public class OrderInfoServiceImpl implements IOrderInfoService
orderInfo.setPayAmt(orderCreateDto.getAmount());
orderInfo.setCreateTime(DateUtils.getNowDate());
orderInfo.setUpdateTime(DateUtils.getNowDate());
return orderInfoMapper.insertOrderInfo(orderInfo);
orderInfo.setOutOrderNo(orderCreateDto.getOutOrderNo());
orderInfoMapper.insertOrderInfo(orderInfo);
return snowflakeIdWorker.nextId();
}
/**
@ -87,11 +85,11 @@ public class OrderInfoServiceImpl implements IOrderInfoService
* @return 结果
*/
@Override
public int updateOrderInfo(OrderInfo orderInfo)
{
public int updateOrderInfo(OrderInfo orderInfo) {
orderInfo.setUpdateTime(DateUtils.getNowDate());
return orderInfoMapper.updateOrderInfo(orderInfo);
}
@Override
public void orderPay(Long orderId) {
OrderInfo updateOrder = this.selectOrderInfoById(orderId);
@ -104,11 +102,22 @@ public class OrderInfoServiceImpl implements IOrderInfoService
updateOrder.setOrderStatus(1L);
updateOrder.setPayTime(DateUtils.getNowDate());
// 修改订单状态
if (1>this.updateOrderInfo(updateOrder)) {
if (1 > this.updateOrderInfo(updateOrder)) {
throw new ServiceException("支付异常,请联系管理员!");
}
}
@Override
public OrderInfo selectOrderInfoByOrderNo(String orderNo) {
return orderInfoMapper.selectOrderInfoByOrderNo(orderNo);
}
@Override
public void insertOrder(OrderInfo orderInfo) {
orderInfoMapper.insertOrderInfo(orderInfo);
}
/**
* 批量删除订单基础信息
*
@ -116,8 +125,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
* @return 结果
*/
@Override
public int deleteOrderInfoByIds(Long[] ids)
{
public int deleteOrderInfoByIds(Long[] ids) {
return orderInfoMapper.deleteOrderInfoByIds(ids);
}
@ -128,8 +136,7 @@ public class OrderInfoServiceImpl implements IOrderInfoService
* @return 结果
*/
@Override
public int deleteOrderInfoById(Long id)
{
public int deleteOrderInfoById(Long id) {
return orderInfoMapper.deleteOrderInfoById(id);
}

View File

@ -35,18 +35,24 @@
<result property="orderNo" column="order_no" />
<result property="orderStatus" column="order_status" />
<result property="orderAmt" column="order_amt" />
<result property="payTime" column="pay_time" />
<result property="productTitle" column="product_title" />
<result property="productPicture" column="product_picture" />
<result property="productDesc" column="product_desc" />
<result property="goodsSpec" column="goods_spec" />
<result property="goodsCount" column="goods_count" />
<result property="subscriptionCancellationTime" column="subscription_cancellation_time" />
<result property="goodsType" column="goods_type" />
<result property="goodsPrice" column="goods_price" />
<result property="goodsCode" column="goods_code" />
</resultMap>
<sql id="selectOrderInfoVo">
select id, order_no, order_status, pay_type, pay_channel, order_amt, freight_amt, pay_amt, really_amt, receive_type, goods_id, business_id, receive_addr_id, create_time, pay_time, create_user_id, update_user_id, is_delete, update_time, track_no, order_type, out_order_no, pay_data, reduction_amout from ORDER_INFO
select id, order_no, order_status, pay_type, pay_channel, order_amt, freight_amt, pay_amt, really_amt, receive_type, goods_id, business_id, receive_addr_id, create_time, pay_time, create_user_id, update_user_id, is_delete, update_time, track_no, order_type, out_order_no, pay_data, reduction_amout from Order_info
</sql>
<sql id="OrderListInfoVo">
SELECT a.order_no,a.order_status,a.order_amt,b.product_title,b.product_picture,b.product_desc,b.goods_spec
FROM ORDER_INFO a LEFT JOIN GOODS_MESSAGES b ON a.goods_id = b.id
SELECT a.id,a.order_no,a.order_status,a.order_amt,a.pay_time,a.goods_count,a.subscription_cancellation_time,a.goods_type,a.goods_price,a.goods_code,b.product_title,b.product_picture,b.product_desc,b.goods_spec
FROM Order_info a LEFT JOIN GOODS_MESSAGES b ON a.goods_id = b.id
</sql>
<select id="selectOrderInfoList" parameterType="OrderInfo" resultMap="OrderInfoResult">
@ -88,9 +94,14 @@
<include refid="selectOrderInfoVo"/>
where id = #{id}
</select>
<select id="selectOrderInfoByOrderNo" resultMap="OrderInfoResult">
<include refid="selectOrderInfoVo"/>
where order_no = #{orderNo}
</select>
<insert id="insertOrderInfo" parameterType="OrderInfo">
insert into ORDER_INFO
insert into Order_info
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="orderNo != null">order_no,</if>
@ -116,6 +127,10 @@
<if test="outOrderNo != null">out_order_no,</if>
<if test="payData != null">pay_data,</if>
<if test="reductionAmout != null">reduction_amout,</if>
<if test="goodsCount != null">goods_count,</if>
<if test="goodsType != null">goods_type,</if>
<if test="goodsPrice != null">goods_price,</if>
<if test="goodsCode != null">goods_code,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
@ -142,11 +157,15 @@
<if test="outOrderNo != null">#{outOrderNo},</if>
<if test="payData != null">#{payData},</if>
<if test="reductionAmout != null">#{reductionAmout},</if>
<if test="goodsCount != null">#{goodsCount},</if>
<if test="goodsType != null">#{goodsType},</if>
<if test="goodsPrice != null">#{goodsPrice},</if>
<if test="goodsCode != null">#{goodsCode},</if>
</trim>
</insert>
<update id="updateOrderInfo" parameterType="OrderInfo">
update ORDER_INFO
update Order_info
<trim prefix="SET" suffixOverrides=",">
<if test="orderNo != null">order_no = #{orderNo},</if>
<if test="orderStatus != null">order_status = #{orderStatus},</if>
@ -176,11 +195,11 @@
</update>
<delete id="deleteOrderInfoById" parameterType="Long">
delete from ORDER_INFO where id = #{id}
delete from Order_info where id = #{id}
</delete>
<delete id="deleteOrderInfoByIds" parameterType="String">
delete from ORDER_INFO where id in
delete from Order_info where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>

50
sf-payment/pom.xml Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>smarterFramework</artifactId>
<groupId>com.smarterFramework</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sf-payment</artifactId>
<description>
支付模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-common</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-framework</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-order</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-service</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.73</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,37 @@
package com.sf.payment.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* 功能描述:
*
* @author a_kun
* @date 2024/4/17 9:23
*/
@Data
@Configuration
public class HuaweiPaymentConfig {
/**
* 客户端idoauth用
*/
@Value("${huawei.payment.clientId:110693217}")
private String clientId;
/**
* 客户端Secret对应各平台的appSecret
*/
@Value("${huawei.payment.clientSecret:1410c01bc71c7ba587175ae79e500137c70945acc1416a38127cf98a09a6f8ba}")
private String clientSecret;
/**
* 应用id
*/
@Value("${huawei.payment.appId:5765880207854169373}")
private String appId;
}

View File

@ -0,0 +1,29 @@
package com.sf.payment.constant;
import io.jsonwebtoken.Claims;
/**
* 商品信息
*
* @author zoukun
*/
public class GoodsConstants {
/**
* 商品类型
* 0消耗型商品
*/
public static final Integer GOODS_TYPE_CONSUMABLE = 0;
/**
* 商品类型
* 1非消耗型商品
*/
public static final Integer GOODS_TYPE_NON_CONSUMABLE = 1;
/**
* 商品类型
* 2自动续期订阅商品
*/
public static final Integer GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION = 2;
}

View File

@ -0,0 +1,48 @@
package com.sf.payment.controller;
import com.alibaba.fastjson2.JSONObject;
import com.sf.common.core.domain.AjaxResult;
import com.sf.payment.constant.GoodsConstants;
import com.sf.payment.domain.HuaweiPaymentCallback;
import com.sf.payment.domain.HuaweiPurchasesVerifyDTO;
import com.sf.payment.service.IHuaweiPaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* @author zoukun
*/
@Slf4j
@RestController
@RequestMapping("/payment")
public class HuaweiPaymentController {
@Autowired
private IHuaweiPaymentService huaweiPaymentService;
/**
* 华为支付回调地址
*/
@RequestMapping("/callback/huawei")
public AjaxResult callback(@RequestBody HuaweiPaymentCallback callback) {
log.info("进入callback params" + JSONObject.toJSONString(callback));
AjaxResult ajax = AjaxResult.success();
return ajax;
}
/**
* 华为购买验证
*/
@PostMapping("/huawei/purchases/verify")
public AjaxResult purchasesVerify(@Validated @RequestBody HuaweiPurchasesVerifyDTO verifyDTO) {
log.info("进入/huawei/purchases/tokens/verify params" + JSONObject.toJSONString(verifyDTO));
huaweiPaymentService.purchasesVerify(verifyDTO);
return AjaxResult.success();
}
}

View File

@ -0,0 +1,35 @@
package com.sf.payment.domain;
import lombok.*;
import java.io.Serializable;
/**
* 授权回调时的参数类
*
* @author zk
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HuaweiPaymentCallback implements Serializable {
/**
* 访问AuthorizeUrl后回调时带的参数code
*/
private String code;
/**
* 客户端id对应各平台的appKey
*/
private String clientId;
/**
* 客户端Secret对应各平台的appSecret
*/
private String clientSecret;
}

View File

@ -0,0 +1,53 @@
package com.sf.payment.domain;
import apijson.NotNull;
import lombok.*;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 华为验证购买tokenDTO
*
* @author zk
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HuaweiPurchasesVerifyDTO implements Serializable {
/**
* 商品类型
* 0消耗型商品
* 1非消耗型商品
* 2自动续期订阅商品
*/
@NotNull
private Integer type;
/**
* 包含订单信息的JWS格式数据
* 可参见对返回结果验签
* 解码验签获取相关购买数据的JSON字符串
* 其包含的参数请参见PurchaseOrderPayload
*/
private String jwsPurchaseOrder;
/**
* 包含订阅状态信息的
* JWS格式数据
* 可参见对返回结果验签
* 解码验签获取相关订阅状态
* 信息的JSON字符串
* 其包含的参数请参见
* SubGroupStatusPayload
*/
private String jwsSubscriptionStatus;
}

View File

@ -0,0 +1,56 @@
package com.sf.payment.domain;
import apijson.NotNull;
import lombok.*;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 华为验证购买tokenD返回
*
* @author zk
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class HuaweiPurchasesVerifyResponseDTO implements Serializable {
/**
* 返回码
*
* 0成功
* 其他失败具体请参见错误码
*/
private String responseCode;
/**
* 响应描述
*/
private String responseMessage;
/**
*
* 包含购买数据的JSON字符串具体请参见表InappPurchaseDetails
*
* 该字段原样参与签名
*/
private String purchaseTokenData;
/**
* purchaseTokenData基于应用RSA IAP私钥的签名信息签名算法为signatureAlgorithm
* 应用请参见对返回结果验签使用IAP公钥对PurchaseTokenData的JSON字符串进行验签
*/
private String dataSignature;
/**
*
* 签名算法
*/
@NotBlank(message = "待下发商品ID不能为空")
private String signatureAlgorithm;
}

View File

@ -0,0 +1,35 @@
package com.sf.payment.domain;
import lombok.Data;
/**
* 功能描述:
*
* @author a_kun
* @date 2024/4/17 14:41
*/
@Data
public class HuaweiQueryResponse {
/**
* 返回码
* 0成功
* 其他失败具体请参见错误码
*/
private String responseCode;
/**
* 响应描述
*/
private String responseMessage;
/**
* 包含已购订单相关状态信息的JWS格式数据可参见对返回结果验签解码验签获取相关订单状态信息的JSON字符串其包含的参数具体请参见表PurchaseOrderPayload说明
*/
private String jwsPurchaseOrder;
/**
* 包含已购订阅相关状态信息的JWS格式数据可参见对返回结果验签解码验签获取相关订阅状态信息的JSON字符串其包含的参数请参见SubGroupStatusPayload
*/
private String jwsSubGroupStatus;
}

View File

@ -0,0 +1,233 @@
package com.sf.payment.domain;
import lombok.Data;
/**
* 功能描述:
*
* @author a_kun
* @date 2024/4/16 15:29
*/
@Data
public class InAppPurchaseData {
/**
* 应用ID
*/
private Long applicationId;
/**
* 消耗型商品或者非消耗型商品固定为false
*
* 订阅型商品
*
* true订阅处于活动状态并且将在下一个结算日期自动续订
* false用户已经取消订阅 用户可以在下一个结算日期之前访问订阅内容并且在该日期后将无法访问除非重新启用自动续订 如果提供了宽限期只要宽限期未过此值就会对所有订阅保持设置为true 下一次结算日期每天都会自动延长直至宽限期结束或者用户更改付款方式
*/
private Boolean autoRenewing;
/**
* 订单ID唯一标识一笔需要收费的收据由华为应用内支付服务器在创建订单以及订阅型商品续费时生成
*
* 每一笔新的收据都会使用不同的orderId
*/
private String orderId;
/**
* 商品类别取值包括
*
* 0消耗型商品
* 1非消耗型商品
* 2订阅型商品
*/
private Integer kind;
/**
* 商品ID每种商品必须有唯一的ID由应用在PMS中维护或者应用发起购买时传入
* 说明
* 为避免资金损失您在对支付结果验签成功后必须对其进行校验
*/
private String productId;
/**
* 商品名称
*/
private String productName;
/**
* 商品购买时间UTC时间戳以毫秒为单位
*/
private Long purchaseTime;
/**
* 订单交易状态
*
* -1初始化
* 0已购买
* 1已取消
* 2已撤销或已退款
* 3待处理
*/
private Integer purchaseState;
/**
*
* 商户侧保留信息由您在调用支付接口时传入
*/
private String developerPayload;
/**
*消耗状态仅一次性商品存在取值包括
*
* 0未消耗
* 1已消耗
*/
private Integer consumptionState;
/**
*确认状态取值包括
*
* 0 未确认
* 1已确认
* 没有值表示不需要确认
*/
private Integer confirmed;
/**
* 用于唯一标识商品和用户对应关系的购买令牌在支付完成时由华为应用内支付服务器生成
* 说明
* 该字段是唯一标识商品和用户对应关系的在订阅型商品正常续订时不会改变
* 当前92位后续存在扩展可能如要进行存储建议您预留128位的长度
* 如要进行存储为保证安全建议加密存储
*/
private String purchaseToken;
/**
* 用定价货币的币种请参见ISO 4217标准
* 说明
* 为避免资金损失您在对支付结果验签成功后必须对其进行校验
*/
private String currency;
/**
* 商品实际价格*100以后的值商品实际价格精确到小数点后2位例如此参数值为501则表示商品实际价格为5.01
*/
private Long price;
/**
* 支付方式取值请参见payType说明
*/
private String payType;
/**
* 交易单号用户支付成功后生成
*/
private String payOrderId;
// 以下参数只在订阅场景返回
/**
* 上次续期收款的订单ID由支付服务器在续期扣费时生成首次购买订阅型商品时的lastOrderId与orderId数值相同
*/
private String lastOrderId;
/**
* 订阅型商品所属的订阅组ID
*/
private String productGroup;
/**
* 原购买的时间即本订阅型商品首次成功收费的时间UTC时间戳以毫秒为单位
*/
private Long oriPurchaseTime;
/**
* 订阅ID
* 说明
* subscriptionId是用户与商品之间的一一对应关系在订阅型商品正常续订时不会改变
*/
private String subscriptionId;
/**
* 原订阅ID有值表示当前订阅是从其他商品切换来的该值可以关联切换前的商品订阅信息
*/
private String oriSubscriptionId;
/**
* 购买数量
*/
private Integer quantity;
/**
* 已经付费订阅的天数免费试用和促销期周期除外
*/
private Long daysLasted;
/**
* 成功标准续期没有设置促销的续期的期数为0或者不存在表示还没有成功续期
*/
private Long numOfPeriods;
/**
* 成功促销续期期数
*/
private Long numOfDiscount;
/**
* 订阅型商品过期时间UTC时间戳以毫秒为单位
*
* 对于一个成功收费的自动续订收据该时间表示续期日期或者超期日期如果商品最近的收据的该时间是一个过去的时间则订阅已经过期
*/
private Long expirationDate;
/**
* 对于已经过期的订阅表示过期原因取值包括
*
* 1用户取消
* 2商品不可用
* 3用户签约信息异常
* 4Billing错误
* 5用户未同意涨价
* 6未知错误
* 同时有多个异常时优先级为1 > 2 > 3
*/
private Integer expirationIntent;
/**
* 订阅撤销时间UTC时间戳以毫秒为单位发生退款且服务立即不可用在顾客投诉通过客服撤销订阅或者顾客升级跨级到同组其他商品并且立即生效场景下需要撤销原有订阅的上次收据时有值
* 说明
* 已经撤销的收据等同于没有完成购买
*/
private Long cancelTime;
/**
* 取消原因
*
* 3: 应用调用IAP接口取消
* 2顾客升级跨级等
* 1顾客因为在App内遇到了问题而取消了订阅
* 0其他原因取消比如顾客错误地订阅了商品
* 说明
* 如果为空且cancelTime有值表示是升级等操作导致的取消
*/
private Integer cancelReason;
/**
* 续期状态
*
* 1当前周期到期时自动续期
* 0用户停止了续期
* 仅针对自动续期订阅对有效和过期的订阅均有效并不代表顾客的订阅状态通常取值为0时应用可以给顾客提供其他的订阅选项
* 例如推荐一个同组更低级别的商品该值为0通常代表着顾客主动取消了该订阅
*/
private Integer renewStatus;
/**
* 用户取消订阅的时间UTC时间戳以毫秒为单位在该时间进行了订阅续期停止的设定商品在有效期内仍然有效但后续的续期会终止无退款
*/
private Integer cancellationTime;
}

View File

@ -0,0 +1,165 @@
package com.sf.payment.domain;
import lombok.Data;
/**
* 功能描述:
* 订单信息模型支持消耗型商品非消耗型商品和自动续期订阅商品
* @author a_kun
* @date 2024/4/16 15:29
*/
@Data
public class PurchaseOrderPayload {
/**
* 具体一笔订单中对应的购买订单号ID
*/
private String purchaseOrderId;
/**
* 购买token
* 在购买消耗型/非消耗型商品场景中与具体购买订单一一对应
* 在自动续期订阅商品场景中与订阅ID一一对应
*/
private String purchaseToken;
/**
* 应用ID
*/
private String applicationId;
/**
* 商品ID每种商品必须有唯一的ID由应用在PMS中维护或者应用发起购买时传入
* 说明
* 为避免资金损失您在对支付结果验签成功后必须对其进行校验
*/
private String productId;
/**
* 商品类型具体取值如下
* 0消耗型商品
* 1非消耗型商品
* 2自动续期订阅商品
*/
private Integer productType;
/**
* 购买时间UTC时间戳以毫秒为单位
* 如果没有完成购买则没有值
*/
private Long purchaseTime;
/**
* 发货状态具体取值如下
* 1已发货
* 2未发货
*/
private Integer finishStatus;
/**
* 价格单位
* 实际价格*100以后的值商品实际价格精确到小数点后2位例如此参数值为501则表示商品实际价格为5.01
*/
private Long price;
/**
* 用定价货币的币种请参见ISO 4217标准
* 说明
* 为避免资金损失您在对支付结果验签成功后必须对其进行校验
*/
private String currency;
/**
* 商户侧保留信息由您在调用支付接口时传入
*/
private String developerPayload;
/**
* 购买订单撤销原因
* 0其他
* 1用户遇到问题退款
*/
private Integer purchaseOrderRevocationReasonCode;
/**
* 购买订单撤销时间
* UTC时间戳以毫秒为单位
*/
private Long revocationTime;
/**
* 优惠类型
* 1推介促销
*/
private Integer offerTypeCode;
/**
* 优惠ID
*/
private String offerId;
/**
* 国家/地区码用于区分国家/地区信息请参见ISO 3166
*/
private String countryCode;
/**
* 签名时间UTC时间戳以毫秒为单位
*/
private Long signedTime;
// 以下参数只在自动续期订阅商品场景返回
/**
* 订阅连续购买段的唯一ID
* 当用户切换商品不会重置此ID
*/
private String subGroupGenerationId;
/**
* 订阅连续购买段的唯一ID
* 当用户切换订阅商品时
* 此订阅ID会发生改变
*/
private String subscriptionId;
/**
* 订阅组ID
*/
private String subGroupId;
/**
* 此次购买的有效周期
* 采用ISO 8601格式
* 例如P1W表示一周
* P1M表示一个月
*/
private String duration;
/**
* 订阅周期段类型
* 0正常周期段
* 1延期周期段
*/
private Integer durationTypeCode;
/*
ISO 8601的时间持续期限表示
在ISO 8601中时间持续期限的表示采用了一种简洁而明确的格式例如 P10D其中 P 表示周期Period后面的数字表示周期的长度而末尾的字母表示周期的单位这种表示法主要用于描述时间段的长度而不关注具体的时刻
P 表示周期Period 此字母指示接下来的时间表示将是一个时间段的描述而非具体的日期或时刻
后面的数字 这个数字表示时间段的长度可以是整数或小数它指示了在时间单位内的周期数量
末尾的字母表示周期的单位 P10D 末尾的 D 表示周期的单位是天DaysISO 8601定义了多种可能的时间单位包括
Y 表示年份例如 P2Y 表示2年的时间段
M 表示月份例如 P3M 表示3个月的时间段
W 表示周数例如 P1W 表示1周的时间段
D 表示天数例如 P10D 表示10天的时间段
T时间分隔符 如果时间段中包含了时间信息日期和时间之间用 T 分隔例如 P1DT12H 表示1天12小时的时间段
H小时M分钟S 用于表示时秒的时间段长度例如 PT2H30M 表示2小时30分钟的时间段
示例
P1Y 表示1年的时间段
P3M 表示3个月的时间段
P2W 表示2周的时间段
P4DT6H30M 表示4天6小时30分钟的时间段
*/
}

View File

@ -0,0 +1,48 @@
package com.sf.payment.domain;
import lombok.Data;
import java.util.List;
/**
* 功能描述:
* 已购订阅相关状态信息
* @author a_kun
* @date 2024/4/16 15:29
*/
@Data
public class SubGroupStatusPayload {
/**
* 应用ID
*/
private String applicationId;
/**
* 应用包名
*/
private String packageName;
/**
* 订阅组ID
*/
private String subGroupId;
/**
* 订阅组中最后生效的
* 订阅状态
* SubscriptionStatus
* 比如A切换BB切换C
* 此处是C的订阅状态
*/
private SubscriptionStatus lastSubscriptionStatus;
/**
* 订阅组最近生效的
* 历史订阅状态
* SubscriptionStatus的列表比如A切换BB切换C这里包含CBA三个订阅状态信息
*/
private List<SubscriptionStatus> historySubscriptionStatusList;
}

View File

@ -0,0 +1,91 @@
package com.sf.payment.domain;
import lombok.Data;
import java.util.List;
/**
* 功能描述:
* 当前订阅最新的未来扣费计划
* @author a_kun
* @date 2024/4/16 15:29
*/
@Data
public class SubRenewalInfo {
/**
* 订阅连续购买段的唯一ID
* 当用户切换商品不会重置此ID
*/
private String subGroupGenerationId;
/**
* 下周期生效场景下下期将续期的商品ID
*/
private String nextRenewPeriodProductId;
/**
* 当前生效的商品ID
*/
private String productId;
/**
* 自动续期状态
* 0关闭
* 1打开
*/
private Integer autoRenewStatusCode;
/**
* 系统是否还在尝试扣费
* true
* false
*/
private Boolean hasInBillingRetryPeriod;
/**
* 目前涨价状态码
* 1用户暂未同意涨价
* 2用户已同意涨价
*/
private Integer priceIncreaseStatusCode;
/**
* 优惠类型
* 1推介促销
*/
private Integer offerTypeCode;
/**
* 优惠ID
*/
private String offerId;
/**
* 下期续费价格单位取消订阅场景下不返回
*/
private String renewalPrice;
/**
* 币种
*/
private String currency;
/**
* 续期时间UTC时间戳以毫秒为单位
*/
private Long renewalTime;
/**
* 订阅续期失败的原因
* 1用户取消
* 2商品无效
* 3签约无效
* 4扣费异常
* 5用户不同意涨价
* 6未知
*/
private String expirationIntent;
}

View File

@ -0,0 +1,68 @@
package com.sf.payment.domain;
import lombok.Data;
import java.util.List;
/**
* 功能描述:
* 订阅组中最后生效的订阅状态
* @author a_kun
* @date 2024/4/16 15:29
*/
@Data
public class SubscriptionStatus {
/**
* 订阅连续购买段的唯一ID
* 当用户切换商品不会重置此ID
*/
private String subGroupGenerationId;
/**
* 订阅连续购买段的唯一ID
* 当用户切换订阅商品时
* 此订阅ID会发生改变
*/
private String subscriptionId;
/**
* 购买token
* 在购买消耗型/非消耗型商品场景中与具体购买订单一一对应
* 在自动续期订阅商品场景中与订阅ID一一对应
*/
private String purchaseToken;
/**
* 订阅状态
* 1生效状态
* 2已到期
* 3尝试扣费
* 5撤销
* 6暂停
*/
private Integer status;
/**
* 自动续期订阅商品的过期时间UTC时间戳以毫秒为单位
*/
private Long expiresTime;
/**
* 当前订阅最新的一笔购买订单包含的参数请参见PurchaseOrderPayload
*/
private PurchaseOrderPayload lastPurchaseOrder;
/**
* 当前订阅最新的购买订单列表包含续期延期折算等产生的购买订单
* 购买订单包含的参数请参见PurchaseOrderPayload
*/
private List<PurchaseOrderPayload> recentPurchaseOrderList;
/**
* 当前订阅最新的未来扣费计划包含的参数请参见SubRenewalInfo
*/
private SubRenewalInfo renewalInfo;
}

View File

@ -0,0 +1,14 @@
package com.sf.payment.service;
import com.sf.payment.domain.HuaweiPurchasesVerifyDTO;
/**
* 功能描述:
*
* @author a_kun
* @date 2024/4/12 10:21
*/
public interface IHuaweiPaymentService {
void purchasesVerify(HuaweiPurchasesVerifyDTO verifyDTO);
}

View File

@ -0,0 +1,395 @@
package com.sf.payment.service.impl;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.sf.common.utils.SecurityUtils;
import com.sf.common.utils.SnowflakeIdWorker;
import com.sf.order.domain.OrderInfo;
import com.sf.order.service.IOrderInfoService;
import com.sf.payment.config.HuaweiPaymentConfig;
import com.sf.payment.constant.GoodsConstants;
import com.sf.payment.domain.*;
import com.sf.payment.service.IHuaweiPaymentService;
import com.sf.payment.utils.HuaweiTokenGenerator;
import com.sf.service.domain.GoodsMessages;
import com.sf.service.service.IGoodsMessagesService;
import com.sf.system.domain.UserMember;
import com.sf.system.service.IUserMemberService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.X509EncodedKeySpec;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 功能描述:
*
* @author a_kun
* @date 2024/4/12 10:21
*/
@Slf4j
@Service
public class HuaweiPaymentServiceImpl implements IHuaweiPaymentService {
/**
* token url to get the authorization
*/
private static final String TOKEN_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
/**
* 查询消耗型/非消耗型商品的订单最新状态
*/
private static final String ORDER_STATUS_QUERY_URL = "/order/harmony/v1/application/order/status/query";
/**
* 查询自动续期订阅商品的最新状态
*/
private static final String SUBSCRIPTION_STATUS_QUERY_URL = "/subscription/harmony/v1/application/subscription/status/query";
/**
* 站点信息(中国)
*/
private static final String ROOT_URL = "https://iap.cloud.huawei.com";
private static final String PUBLIC_KEY = "PUBLIC_KEY";
@Resource
private IOrderInfoService orderInfoService;
@Resource
private IUserMemberService userMemberService;
@Resource
private IGoodsMessagesService goodsMessagesService;
@Resource
private HuaweiPaymentConfig huaweiPaymentConfig;
@Resource
private SnowflakeIdWorker snowflakeIdWorker;
@Override
@Transactional(rollbackFor = Exception.class)
public void purchasesVerify(HuaweiPurchasesVerifyDTO verifyDTO) {
// 待发放会员商品
PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload;
// 验证 TODO 证书验签官网未实现
if (GoodsConstants.GOODS_TYPE_CONSUMABLE.equals(verifyDTO.getType())
|| GoodsConstants.GOODS_TYPE_NON_CONSUMABLE.equals(verifyDTO.getType())) {
Assert.hasText(verifyDTO.getJwsPurchaseOrder(), "订单信息不能为空");
// 消耗/非消耗商品购买验证
consumablePurchasesVerify(verifyDTO.getJwsPurchaseOrder());
} else if (GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(verifyDTO.getType())) {
Assert.hasText(verifyDTO.getJwsSubscriptionStatus(), "订单信息不能为空");
// 订阅商品购买验证
subscriptionPurchasesVerify(verifyDTO.getJwsSubscriptionStatus());
} else {
throw new IllegalArgumentException("商品类型错误!");
}
}
private PurchaseOrderPayload subscriptionPurchasesVerify(String jwsSubscriptionStatus) {
DecodedJWT decodedJWT = JWT.decode(jwsSubscriptionStatus);
//String header = decodedJWT.getHeader();
String payload = decodedJWT.getPayload();
// 前面应该不是base64编码后的官网说都是编码后的但是解码会报错
//String signature = decodedJWT.getSignature();
String decodeAppPayload = new String(Base64Utils.decode(payload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
// String decodeHeader = new String(Base64Utils.decode(header.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
// String decodeSignature = new String(Base64Utils.decode(signature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
SubGroupStatusPayload appSubGroupStatusPayload = JSON.parseObject(decodeAppPayload, SubGroupStatusPayload.class);
SubscriptionStatus lastSubscriptionStatus = appSubGroupStatusPayload.getLastSubscriptionStatus();
PurchaseOrderPayload lastPurchaseOrder = lastSubscriptionStatus.getLastPurchaseOrder();
// pack the request body
Map<String, String> bodyMap = new HashMap<>();
bodyMap.put("purchaseToken", lastPurchaseOrder.getPurchaseToken());
bodyMap.put("purchaseOrderId", lastPurchaseOrder.getPurchaseOrderId());
// construct the Authorization in Header
Map<String, String> headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap);
// 订阅状态查询
String response = HttpUtil.createPost(ROOT_URL + SUBSCRIPTION_STATUS_QUERY_URL)
.addHeaders(headers)
.body(JSON.toJSONString(bodyMap))
.execute().body();
log.info("订单状态查询返回信息:{}", response);
HuaweiQueryResponse huaweiQueryResponse = JSON.parseObject(response, HuaweiQueryResponse.class);
if (!"0".equals(huaweiQueryResponse.getResponseCode())) {
throw new RuntimeException("订单状态查询失败");
}
DecodedJWT huaweiQueryResponseDecodedJWT = JWT.decode(huaweiQueryResponse.getJwsSubGroupStatus());
String huaweiQueryResponsepayload = huaweiQueryResponseDecodedJWT.getPayload();
String decodeHuaweiQueryResponsepayload = new String(Base64Utils.decode(huaweiQueryResponsepayload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
SubGroupStatusPayload subGroupStatusPayload = JSON.parseObject(decodeHuaweiQueryResponsepayload, SubGroupStatusPayload.class);
PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload = subGroupStatusPayload.getLastSubscriptionStatus().getLastPurchaseOrder();
// 发货
return delivery(lastPurchaseOrder, huaweiQueryResponsePurchaseOrderPayload);
}
/**
* 发货
* @param purchaseOrder
* @param huaweiQueryResponsePurchaseOrderPayload
* @return
*/
private PurchaseOrderPayload delivery(PurchaseOrderPayload purchaseOrder, PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload) {
Assert.isTrue(purchaseOrder.getPurchaseOrderId().equals(huaweiQueryResponsePurchaseOrderPayload.getPurchaseOrderId()), "订单不一致,发货失败!");
if (2 == huaweiQueryResponsePurchaseOrderPayload.getFinishStatus()) {
// 还未发货
// 查询平台是否配置该商品
// 查询平台是否配置该商品
GoodsMessages goods = goodsMessagesService.selectGoodsMessagesByCode(huaweiQueryResponsePurchaseOrderPayload.getProductId());
Assert.notNull(goods, "未配置此商品,请检查商品配置");
Assert.isTrue(goods.getOriginalPrice().equals(huaweiQueryResponsePurchaseOrderPayload.getPrice()), "商品价格与订单价格不一致,请检查价格配置");
// 创建完成订单
createOrder(huaweiQueryResponsePurchaseOrderPayload, goods);
// 发放会员权益
distributeMembershipBenefits(huaweiQueryResponsePurchaseOrderPayload);
} else {
log.info("华为应用内支付订单已发货!{}", JSON.toJSONString(huaweiQueryResponsePurchaseOrderPayload));
}
return huaweiQueryResponsePurchaseOrderPayload;
}
private PurchaseOrderPayload consumablePurchasesVerify(String jwsPurchaseOrder) {
DecodedJWT decodedJWT = JWT.decode(jwsPurchaseOrder);
//String header = decodedJWT.getHeader();
String payload = decodedJWT.getPayload();
// 前面应该不是base64编码后的官网说都是编码后的但是解码会报错
//String signature = decodedJWT.getSignature();
String decodeAppPayload = new String(Base64Utils.decode(payload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
// String decodeHeader = new String(Base64Utils.decode(header.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
// String decodeSignature = new String(Base64Utils.decode(signature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
PurchaseOrderPayload appPurchaseOrderPayload = JSON.parseObject(decodeAppPayload, PurchaseOrderPayload.class);
// pack the request body
Map<String, String> bodyMap = new HashMap<>();
bodyMap.put("purchaseToken", appPurchaseOrderPayload.getPurchaseToken());
bodyMap.put("purchaseOrderId", appPurchaseOrderPayload.getPurchaseOrderId());
// construct the Authorization in Header
Map<String, String> headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap);
// 订单状态查询
String response = HttpUtil.createPost(ROOT_URL + ORDER_STATUS_QUERY_URL)
.addHeaders(headers)
.body(JSON.toJSONString(bodyMap))
.execute().body();
log.info("订单状态查询返回信息:{}", response);
HuaweiQueryResponse huaweiQueryResponse = JSON.parseObject(response, HuaweiQueryResponse.class);
if (!"0".equals(huaweiQueryResponse.getResponseCode())) {
throw new RuntimeException("订单状态查询失败");
}
DecodedJWT huaweiQueryResponseDecodedJWT = JWT.decode(huaweiQueryResponse.getJwsPurchaseOrder());
// String huaweiQueryResponseHeader = huaweiQueryResponseDecodedJWT.getHeader();
String huaweiQueryResponsepayload = huaweiQueryResponseDecodedJWT.getPayload();
// String huaweiQueryResponsesignature = huaweiQueryResponseDecodedJWT.getSignature();
//String decodehuaweiQueryResponseHeader = new String(Base64Utils.decode(huaweiQueryResponseHeader.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
String decodehuaweiQueryResponsepayload = new String(Base64Utils.decode(huaweiQueryResponsepayload.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
// String decodehuaweiQueryResponsesignature = new String(Base64Utils.decode(huaweiQueryResponsesignature.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload = JSON.parseObject(decodehuaweiQueryResponsepayload, PurchaseOrderPayload.class);
// 暂时只做简单验证
return delivery(appPurchaseOrderPayload, huaweiQueryResponsePurchaseOrderPayload);
}
private void createOrder(PurchaseOrderPayload appPurchaseOrderPayload, GoodsMessages goods) {
Long userId = SecurityUtils.getUserId();
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderNo(snowflakeIdWorker.nextId());
orderInfo.setOrderStatus(6L);
orderInfo.setPayType(1L);
orderInfo.setPayChannel(2L);
orderInfo.setOrderAmt(appPurchaseOrderPayload.getPrice());
orderInfo.setPayAmt(appPurchaseOrderPayload.getPrice());
orderInfo.setReallyAmt(appPurchaseOrderPayload.getPrice());
orderInfo.setReceiveType(0L);
orderInfo.setGoodsId(goods.getId());
orderInfo.setPayTime(DateUtil.date(appPurchaseOrderPayload.getPurchaseTime()));
orderInfo.setCreateUserId(userId);
orderInfo.setUpdateUserId(userId);
orderInfo.setOutOrderNo(appPurchaseOrderPayload.getPurchaseOrderId());
orderInfo.setPayData(JSON.toJSONString(appPurchaseOrderPayload));
orderInfo.setGoodsPrice(goods.getOriginalPrice());
orderInfo.setGoodsType(goods.getGoodsType());
orderInfo.setGoodsCode(goods.getGoodsCode());
orderInfoService.insertOrder(orderInfo);
}
private void distributeMembershipBenefits(PurchaseOrderPayload purchaseOrderPayload) {
// 发放会员权益
Long userId = SecurityUtils.getUserId();
UserMember userMember = userMemberService.selectUserMemberByUserId(userId);
if (userMember == null) {
// 添加会员信息
boolean isSubscription = GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(purchaseOrderPayload.getProductType());
DateTime payTime = DateUtil.date(purchaseOrderPayload.getPurchaseTime());
userMember = new UserMember();
userMember.setMemberLevel(isSubscription ? 1 : 2);
userMember.setSubscriptionStatus(isSubscription ? 1 : 0);
userMember.setUserId(userId);
userMember.setIntegration(0L);
DateTime expirationTime = DateUtil.offset(payTime, DateField.MONTH, 1);
userMember.setExpirationTime(expirationTime);
userMember.setCreateTime(new Date());
userMember.setUpdateTime(new Date());
userMemberService.insertUserMember(userMember);
} else {
// 更新
DateTime expirationTime = DateUtil.offset(userMember.getExpirationTime(), DateField.MONTH, 1);
userMember.setExpirationTime(expirationTime);
userMember.setUpdateTime(new Date());
userMemberService.updateUserMember(userMember);
}
}
/**
* Build Authorization in Header
*
* @return headers
*/
public static Map<String, String> buildAuthorization(String appId, Map<String, String> body) {
Map<String, Object> jwtHeader = new HashMap<>(8);
jwtHeader.put("alg", "ES256");
jwtHeader.put("typ", "JWT");
jwtHeader.put("kid", "0ae3e1be-374b-43a5-a297-045addbf76eb");
Map<String, Object> jwtPayload = new HashMap<>(8);
jwtPayload.put("iss", "f59509e6-dd17-4644-b832-ff05233146c8");
jwtPayload.put("aud", "iap-v1");
jwtPayload.put("iat", DateUtil.currentSeconds());
jwtPayload.put("exp", DateUtil.currentSeconds() + 1800L); // 半小时过期
jwtPayload.put("aid", appId);
jwtPayload.put("digest", getJwtPayloadDigest(body));
String token = HuaweiTokenGenerator.createToken(jwtHeader, jwtPayload);
// String authorization = MessageFormat.format("Basic {0}", Base64.encodeBase64String(oriString.getBytes(StandardCharsets.UTF_8)));
String authorization = MessageFormat.format("Bearer {0}", token);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", authorization);
headers.put("Content-Type", "application/json; charset=UTF-8");
return headers;
}
private static String getJwtPayloadDigest(Map<String, String> body) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(JSON.toJSONString(body).getBytes(StandardCharsets.UTF_8));
byte[] digestByte = messageDigest.digest();
StringBuilder stringBuffer = new StringBuilder();
String temp;
for (byte aByte : digestByte) {
temp = Integer.toHexString(aByte & 0xFF);
if (temp.length() == 1) {
stringBuffer.append("0");
}
stringBuffer.append(temp);
}
return stringBuffer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 校验签名信息校验InAppPurchaseData中的productIdpricecurrency等信息的一致性
*
* @param content 结果字符串
* @param sign 签名字符串
* @param publicKey IAP公钥
* @return 是否校验通过
*/
public static boolean checkSuccessOrder(String content, String sign, String publicKey, String signatureAlgorithm, OrderInfo orderInfo) {
// 校验签名信息
boolean checkRes = checkSign(content, sign, publicKey, StrUtil.blankToDefault(signatureAlgorithm, "SHA256WithRSA"));
if (checkRes) {
// 校验InAppPurchaseData中的productIdpricecurrency等信息的一致性
checkRes = checkProductIdAndPriceAndCurrency(content, orderInfo);
}
return checkRes;
}
/**
* 校验InAppPurchaseData中的productIdpricecurrency等信息的一致性
*
* @param content 结果字符串
* @param orderInfo 您的订单信息包括productIdpricecurrency
* @return 是否校验通过
*/
public static boolean checkProductIdAndPriceAndCurrency(String content, OrderInfo orderInfo) {
InAppPurchaseData inAppPurchaseData = JSON.parseObject(content, InAppPurchaseData.class);
// 校验InAppPurchaseData中的productIdpricecurrency等信息的一致性
return inAppPurchaseData.getProductId().equals(orderInfo.getGoodsId())
&& inAppPurchaseData.getPrice().equals(orderInfo.getOrderAmt());
}
/**
* 校验签名信息
*
* @param content 结果字符串
* @param sign 签名字符串
* @param publicKey IAP公钥
* @param signatureAlgorithm 签名算法字段可从接口返回数据中获取例如OwnedPurchasesResult.getSignatureAlgorithm()
* @return 是否校验通过
*/
public static boolean checkSign(String content, String sign, String publicKey, String signatureAlgorithm) {
if (sign == null) {
return false;
}
if (publicKey == null) {
return false;
}
// 当signatureAlgorithm为空时使用默认签名算法
if (signatureAlgorithm == null || signatureAlgorithm.length() == 0) {
signatureAlgorithm = "SHA256WithRSA";
System.out.println("doCheck, algorithm: SHA256WithRSA");
}
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// 生成"RSA"的KeyFactory对象
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decodedKey = Base64.decodeBase64(publicKey);
// 生成公钥
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
java.security.Signature signature = null;
// 根据SHA256WithRSA算法获取签名对象实例
signature = java.security.Signature.getInstance(signatureAlgorithm);
// 初始化验证签名的公钥
signature.initVerify(pubKey);
// 把原始报文更新到签名对象中
signature.update(content.getBytes(StandardCharsets.UTF_8));
// 将sign解码
byte[] bsign = Base64.decodeBase64(sign);
// 进行验签
return signature.verify(bsign);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

View File

@ -0,0 +1,43 @@
package com.sf.payment.utils;
import cn.hutool.core.io.resource.ClassPathResource;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
public class HuaweiTokenGenerator {
public static String createToken(Map<String, Object> jwtHeader, Map<String, Object> jwtPayload) {
try {
// AppGallery Connect 华为应用内支付配置密钥下载私钥文件
ClassPathResource classPathResource = new ClassPathResource("IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8");
InputStream IAPKeyStream = classPathResource.getStream();
String content = IOUtils.toString(IAPKeyStream, String.valueOf(StandardCharsets.UTF_8));
String privateKey = content
.replace("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\\R+", "")
.replace("-----END PRIVATE KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("EC");
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
ECPrivateKey ecPrivateKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
return JWT.create()
.withHeader(jwtHeader)
.withPayload(jwtPayload)
.sign(Algorithm.ECDSA256(ecPrivateKey));
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQggG04243qynU/yWYy
XpYVy9ZWMuLKzZiwhXCBWQBCOLigCgYIKoZIzj0DAQehRANCAARNln2/d+TM2pIO
LWQzvI77gPAVEvVCSlIuiJ+J7CJSG5KCysBaEeiiD5cc4dZWnUBijF8FBh7nDLaH
VwFXfrS+
-----END PRIVATE KEY-----

33
sf-service/pom.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>smarterFramework</artifactId>
<groupId>com.smarterFramework</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sf-service</artifactId>
<description>
业务模块
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-common</artifactId>
</dependency>
<dependency>
<groupId>com.smarterFramework</groupId>
<artifactId>sf-framework</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,7 +1,10 @@
package com.sf.goods.controller;
package com.sf.service.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.sf.service.domain.GoodsMessages;
import com.sf.service.service.IGoodsMessagesService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@ -16,19 +19,17 @@ import com.sf.common.annotation.Log;
import com.sf.common.core.controller.BaseController;
import com.sf.common.core.domain.AjaxResult;
import com.sf.common.enums.BusinessType;
import com.sf.goods.domain.GoodsMessages;
import com.sf.goods.service.IGoodsMessagesService;
import com.sf.common.utils.poi.ExcelUtil;
import com.sf.common.core.page.TableDataInfo;
/**
* 商品信息Controller
*
* @author ztzh
* @date 2024-04-11
*
* @author zoukun
* @date 2024-04-18
*/
@RestController
@RequestMapping("/goods/goods")
@RequestMapping("/service/goods")
public class GoodsMessagesController extends BaseController
{
@Autowired
@ -37,7 +38,6 @@ public class GoodsMessagesController extends BaseController
/**
* 查询商品信息列表
*/
@PreAuthorize("@ss.hasPermi('goods:goods:list')")
@GetMapping("/list")
public TableDataInfo list(GoodsMessages goodsMessages)
{
@ -49,7 +49,7 @@ public class GoodsMessagesController extends BaseController
/**
* 导出商品信息列表
*/
@PreAuthorize("@ss.hasPermi('goods:goods:export')")
@PreAuthorize("@ss.hasPermi('service:goods:export')")
@Log(title = "商品信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, GoodsMessages goodsMessages)
@ -62,7 +62,7 @@ public class GoodsMessagesController extends BaseController
/**
* 获取商品信息详细信息
*/
@PreAuthorize("@ss.hasPermi('goods:goods:query')")
@PreAuthorize("@ss.hasPermi('service:goods:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id)
{
@ -72,7 +72,7 @@ public class GoodsMessagesController extends BaseController
/**
* 新增商品信息
*/
@PreAuthorize("@ss.hasPermi('goods:goods:add')")
@PreAuthorize("@ss.hasPermi('service:goods:add')")
@Log(title = "商品信息", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody GoodsMessages goodsMessages)
@ -83,7 +83,7 @@ public class GoodsMessagesController extends BaseController
/**
* 修改商品信息
*/
@PreAuthorize("@ss.hasPermi('goods:goods:edit')")
@PreAuthorize("@ss.hasPermi('service:goods:edit')")
@Log(title = "商品信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody GoodsMessages goodsMessages)
@ -94,9 +94,9 @@ public class GoodsMessagesController extends BaseController
/**
* 删除商品信息
*/
@PreAuthorize("@ss.hasPermi('goods:goods:remove')")
@PreAuthorize("@ss.hasPermi('service:goods:remove')")
@Log(title = "商品信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{
return toAjax(goodsMessagesService.deleteGoodsMessagesByIds(ids));

View File

@ -1,4 +1,4 @@
package com.sf.goods.domain;
package com.sf.service.domain;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -8,8 +8,8 @@ import com.sf.common.core.domain.BaseEntity;
/**
* 商品信息对象 GOODS_MESSAGES
*
* @author ztzh
* @date 2024-04-11
* @author zoukun
* @date 2024-04-18
*/
public class GoodsMessages extends BaseEntity
{
@ -26,6 +26,7 @@ public class GoodsMessages extends BaseEntity
private Long stockId;
/** 审核状态1通过0未通过 */
@Excel(name = "审核状态1通过0未通过")
private Long reviewStatus;
/** 商品标题 */
@ -36,16 +37,23 @@ public class GoodsMessages extends BaseEntity
@Excel(name = "商品图片")
private String productPicture;
/** 原价 */
@Excel(name = "原价")
/** 商品原价 */
@Excel(name = "商品原价")
private Long originalPrice;
/** 商品描述 */
@Excel(name = "商品描述")
private String productDesc;
/** 商品类型。 * • 0消耗型商品 * • 1非消耗型商品 * • 2自动续期订阅商品 */
@Excel(name = "商品类型。 * • 0消耗型商品 * • 1非消耗型商品 * • 2自动续期订阅商品")
private Integer goodsType;
/** 商品规格 */
@Excel(name = "商品规格")
private String goodsSpec;
/** 排序 */
@Excel(name = "排序")
private Long orderNum;
/** 逻辑删除,0:未删除,1:删除 */
@ -57,13 +65,12 @@ public class GoodsMessages extends BaseEntity
/** 更新人 */
private String modified;
/** 商品类型 */
@Excel(name = "商品类型")
private String goodsType;
/** 商品名称 */
private String goodsName;
/** 商品规格 */
@Excel(name = "商品规格")
private String goodsSpec;
/** 商品型号 */
@Excel(name = "商品型号")
private String goodsModel;
public void setId(Long id)
{
@ -137,6 +144,24 @@ public class GoodsMessages extends BaseEntity
{
return productDesc;
}
public void setGoodsType(Integer goodsType)
{
this.goodsType = goodsType;
}
public Integer getGoodsType()
{
return goodsType;
}
public void setGoodsSpec(String goodsSpec)
{
this.goodsSpec = goodsSpec;
}
public String getGoodsSpec()
{
return goodsSpec;
}
public void setOrderNum(Long orderNum)
{
this.orderNum = orderNum;
@ -173,23 +198,23 @@ public class GoodsMessages extends BaseEntity
{
return modified;
}
public void setGoodsType(String goodsType)
public void setGoodsName(String goodsName)
{
this.goodsType = goodsType;
this.goodsName = goodsName;
}
public String getGoodsType()
public String getGoodsName()
{
return goodsType;
return goodsName;
}
public void setGoodsSpec(String goodsSpec)
public void setGoodsModel(String goodsModel)
{
this.goodsSpec = goodsSpec;
this.goodsModel = goodsModel;
}
public String getGoodsSpec()
public String getGoodsModel()
{
return goodsSpec;
return goodsModel;
}
@Override
@ -203,14 +228,16 @@ public class GoodsMessages extends BaseEntity
.append("productPicture", getProductPicture())
.append("originalPrice", getOriginalPrice())
.append("productDesc", getProductDesc())
.append("goodsType", getGoodsType())
.append("goodsSpec", getGoodsSpec())
.append("orderNum", getOrderNum())
.append("isDelete", getIsDelete())
.append("created", getCreated())
.append("modified", getModified())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.append("goodsType", getGoodsType())
.append("goodsSpec", getGoodsSpec())
.append("goodsName", getGoodsName())
.append("goodsModel", getGoodsModel())
.toString();
}
}

View File

@ -1,13 +1,13 @@
package com.sf.goods.mapper;
package com.sf.service.mapper;
import java.util.List;
import com.sf.goods.domain.GoodsMessages;
import com.sf.service.domain.GoodsMessages;
/**
* 商品信息Mapper接口
*
* @author ztzh
* @date 2024-04-11
* @author zoukun
* @date 2024-04-18
*/
public interface GoodsMessagesMapper
{
@ -58,4 +58,6 @@ public interface GoodsMessagesMapper
* @return 结果
*/
public int deleteGoodsMessagesByIds(Long[] ids);
GoodsMessages selectGoodsMessagesByCode(String goodsCode);
}

View File

@ -1,13 +1,13 @@
package com.sf.goods.service;
package com.sf.service.service;
import java.util.List;
import com.sf.goods.domain.GoodsMessages;
import com.sf.service.domain.GoodsMessages;
/**
* 商品信息Service接口
*
* @author ztzh
* @date 2024-04-11
* @author zoukun
* @date 2024-04-18
*/
public interface IGoodsMessagesService
{
@ -58,4 +58,6 @@ public interface IGoodsMessagesService
* @return 结果
*/
public int deleteGoodsMessagesById(Long id);
GoodsMessages selectGoodsMessagesByCode(String goodsCode);
}

View File

@ -1,18 +1,19 @@
package com.sf.goods.service.impl;
package com.sf.service.service.impl;
import java.util.List;
import com.sf.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sf.goods.mapper.GoodsMessagesMapper;
import com.sf.goods.domain.GoodsMessages;
import com.sf.goods.service.IGoodsMessagesService;
import com.sf.service.mapper.GoodsMessagesMapper;
import com.sf.service.domain.GoodsMessages;
import com.sf.service.service.IGoodsMessagesService;
import org.springframework.util.StringUtils;
/**
* 商品信息Service业务层处理
*
* @author ztzh
* @date 2024-04-11
* @author zoukun
* @date 2024-04-18
*/
@Service
public class GoodsMessagesServiceImpl implements IGoodsMessagesService
@ -93,4 +94,12 @@ public class GoodsMessagesServiceImpl implements IGoodsMessagesService
{
return goodsMessagesMapper.deleteGoodsMessagesById(id);
}
@Override
public GoodsMessages selectGoodsMessagesByCode(String goodsCode) {
if (StringUtils.hasText(goodsCode)){
return goodsMessagesMapper.selectGoodsMessagesByCode(goodsCode);
}
return null;
}
}

View File

@ -1,118 +1,130 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sf.goods.mapper.GoodsMessagesMapper">
<resultMap type="GoodsMessages" id="GoodsMessagesResult">
<result property="id" column="id" />
<result property="goodsCode" column="goods_code" />
<result property="stockId" column="stock_id" />
<result property="reviewStatus" column="review_status" />
<result property="productTitle" column="product_title" />
<result property="productPicture" column="product_picture" />
<result property="originalPrice" column="original_price" />
<result property="productDesc" column="product_desc" />
<result property="orderNum" column="order_num" />
<result property="isDelete" column="is_delete" />
<result property="created" column="created" />
<result property="modified" column="modified" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="goodsType" column="goods_type" />
<result property="goodsSpec" column="goods_spec" />
</resultMap>
<sql id="selectGoodsMessagesVo">
select id, goods_code, stock_id, review_status, product_title, product_picture, original_price, product_desc, order_num, is_delete, created, modified, create_time, update_time, goods_type, goods_spec from GOODS_MESSAGES
</sql>
<select id="selectGoodsMessagesList" parameterType="GoodsMessages" resultMap="GoodsMessagesResult">
<include refid="selectGoodsMessagesVo"/>
<where>
<if test="goodsCode != null and goodsCode != ''"> and goods_code = #{goodsCode}</if>
<if test="productTitle != null and productTitle != ''"> and product_title like concat('%', #{productTitle}, '%')</if>
<if test="goodsType != null and goodsType != ''"> and goods_type = #{goodsType}</if>
</where>
</select>
<select id="selectGoodsMessagesById" parameterType="Long" resultMap="GoodsMessagesResult">
<include refid="selectGoodsMessagesVo"/>
where id = #{id}
</select>
<select id="selectGoodsMessagesByCode" parameterType="java.lang.String" resultMap="GoodsMessagesResult">
<include refid="selectGoodsMessagesVo"/>
where goods_code = #{code}
</select>
<insert id="insertGoodsMessages" parameterType="GoodsMessages" useGeneratedKeys="true" keyProperty="id">
insert into GOODS_MESSAGES
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="goodsCode != null and goodsCode != ''">goods_code,</if>
<if test="stockId != null">stock_id,</if>
<if test="reviewStatus != null">review_status,</if>
<if test="productTitle != null and productTitle != ''">product_title,</if>
<if test="productPicture != null and productPicture != ''">product_picture,</if>
<if test="originalPrice != null">original_price,</if>
<if test="productDesc != null">product_desc,</if>
<if test="orderNum != null">order_num,</if>
<if test="isDelete != null">is_delete,</if>
<if test="created != null">created,</if>
<if test="modified != null">modified,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
<if test="goodsType != null and goodsType != ''">goods_type,</if>
<if test="goodsSpec != null and goodsSpec != ''">goods_spec,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="goodsCode != null and goodsCode != ''">#{goodsCode},</if>
<if test="stockId != null">#{stockId},</if>
<if test="reviewStatus != null">#{reviewStatus},</if>
<if test="productTitle != null and productTitle != ''">#{productTitle},</if>
<if test="productPicture != null and productPicture != ''">#{productPicture},</if>
<if test="originalPrice != null">#{originalPrice},</if>
<if test="productDesc != null">#{productDesc},</if>
<if test="orderNum != null">#{orderNum},</if>
<if test="isDelete != null">#{isDelete},</if>
<if test="created != null">#{created},</if>
<if test="modified != null">#{modified},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="goodsType != null and goodsType != ''">#{goodsType},</if>
<if test="goodsSpec != null and goodsSpec != ''">#{goodsSpec},</if>
</trim>
</insert>
<update id="updateGoodsMessages" parameterType="GoodsMessages">
update GOODS_MESSAGES
<trim prefix="SET" suffixOverrides=",">
<if test="goodsCode != null and goodsCode != ''">goods_code = #{goodsCode},</if>
<if test="stockId != null">stock_id = #{stockId},</if>
<if test="reviewStatus != null">review_status = #{reviewStatus},</if>
<if test="productTitle != null and productTitle != ''">product_title = #{productTitle},</if>
<if test="productPicture != null and productPicture != ''">product_picture = #{productPicture},</if>
<if test="originalPrice != null">original_price = #{originalPrice},</if>
<if test="productDesc != null">product_desc = #{productDesc},</if>
<if test="orderNum != null">order_num = #{orderNum},</if>
<if test="isDelete != null">is_delete = #{isDelete},</if>
<if test="created != null">created = #{created},</if>
<if test="modified != null">modified = #{modified},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="goodsType != null and goodsType != ''">goods_type = #{goodsType},</if>
<if test="goodsSpec != null and goodsSpec != ''">goods_spec = #{goodsSpec},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteGoodsMessagesById" parameterType="Long">
delete from GOODS_MESSAGES where id = #{id}
</delete>
<delete id="deleteGoodsMessagesByIds" parameterType="String">
delete from GOODS_MESSAGES where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sf.service.mapper.GoodsMessagesMapper">
<resultMap type="GoodsMessages" id="GoodsMessagesResult">
<result property="id" column="id" />
<result property="goodsCode" column="goods_code" />
<result property="stockId" column="stock_id" />
<result property="reviewStatus" column="review_status" />
<result property="productTitle" column="product_title" />
<result property="productPicture" column="product_picture" />
<result property="originalPrice" column="original_price" />
<result property="productDesc" column="product_desc" />
<result property="goodsType" column="goods_type" />
<result property="goodsSpec" column="goods_spec" />
<result property="orderNum" column="order_num" />
<result property="isDelete" column="is_delete" />
<result property="created" column="created" />
<result property="modified" column="modified" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="goodsName" column="goods_name" />
<result property="goodsModel" column="goods_model" />
</resultMap>
<sql id="selectGoodsMessagesVo">
select id, goods_code, stock_id, review_status, product_title, product_picture, original_price, product_desc, goods_type, goods_spec, order_num, is_delete, created, modified, create_time, update_time, goods_name, goods_model from GOODS_MESSAGES
</sql>
<select id="selectGoodsMessagesList" parameterType="GoodsMessages" resultMap="GoodsMessagesResult">
<include refid="selectGoodsMessagesVo"/>
<where>
<if test="goodsCode != null and goodsCode != ''"> and goods_code = #{goodsCode}</if>
<if test="reviewStatus != null "> and review_status = #{reviewStatus}</if>
<if test="productTitle != null and productTitle != ''"> and product_title like concat('%', #{productTitle}, '%')</if>
<if test="params.beginOriginalPrice != null and params.beginOriginalPrice != '' and params.endOriginalPrice != null and params.endOriginalPrice != ''"> and original_price between #{params.beginOriginalPrice} and #{params.endOriginalPrice}</if>
<if test="goodsType != null "> and goods_type = #{goodsType}</if>
<if test="goodsSpec != null and goodsSpec != ''"> and goods_spec = #{goodsSpec}</if>
<if test="goodsModel != null and goodsModel != ''"> and goods_model = #{goodsModel}</if>
</where>
</select>
<select id="selectGoodsMessagesById" parameterType="Long" resultMap="GoodsMessagesResult">
<include refid="selectGoodsMessagesVo"/>
where id = #{id}
</select>
<select id="selectGoodsMessagesByCode" parameterType="String" resultMap="GoodsMessagesResult">
<include refid="selectGoodsMessagesVo"/>
where goods_code = #{goodsCode}
</select>
<insert id="insertGoodsMessages" parameterType="GoodsMessages" useGeneratedKeys="true" keyProperty="id">
insert into GOODS_MESSAGES
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="goodsCode != null and goodsCode != ''">goods_code,</if>
<if test="stockId != null">stock_id,</if>
<if test="reviewStatus != null">review_status,</if>
<if test="productTitle != null and productTitle != ''">product_title,</if>
<if test="productPicture != null and productPicture != ''">product_picture,</if>
<if test="originalPrice != null">original_price,</if>
<if test="productDesc != null">product_desc,</if>
<if test="goodsType != null">goods_type,</if>
<if test="goodsSpec != null and goodsSpec != ''">goods_spec,</if>
<if test="orderNum != null">order_num,</if>
<if test="isDelete != null">is_delete,</if>
<if test="created != null">created,</if>
<if test="modified != null">modified,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
<if test="goodsName != null and goodsName != ''">goods_name,</if>
<if test="goodsModel != null">goods_model,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="goodsCode != null and goodsCode != ''">#{goodsCode},</if>
<if test="stockId != null">#{stockId},</if>
<if test="reviewStatus != null">#{reviewStatus},</if>
<if test="productTitle != null and productTitle != ''">#{productTitle},</if>
<if test="productPicture != null and productPicture != ''">#{productPicture},</if>
<if test="originalPrice != null">#{originalPrice},</if>
<if test="productDesc != null">#{productDesc},</if>
<if test="goodsType != null">#{goodsType},</if>
<if test="goodsSpec != null and goodsSpec != ''">#{goodsSpec},</if>
<if test="orderNum != null">#{orderNum},</if>
<if test="isDelete != null">#{isDelete},</if>
<if test="created != null">#{created},</if>
<if test="modified != null">#{modified},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="goodsName != null and goodsName != ''">#{goodsName},</if>
<if test="goodsModel != null">#{goodsModel},</if>
</trim>
</insert>
<update id="updateGoodsMessages" parameterType="GoodsMessages">
update GOODS_MESSAGES
<trim prefix="SET" suffixOverrides=",">
<if test="goodsCode != null and goodsCode != ''">goods_code = #{goodsCode},</if>
<if test="stockId != null">stock_id = #{stockId},</if>
<if test="reviewStatus != null">review_status = #{reviewStatus},</if>
<if test="productTitle != null and productTitle != ''">product_title = #{productTitle},</if>
<if test="productPicture != null and productPicture != ''">product_picture = #{productPicture},</if>
<if test="originalPrice != null">original_price = #{originalPrice},</if>
<if test="productDesc != null">product_desc = #{productDesc},</if>
<if test="goodsType != null">goods_type = #{goodsType},</if>
<if test="goodsSpec != null and goodsSpec != ''">goods_spec = #{goodsSpec},</if>
<if test="orderNum != null">order_num = #{orderNum},</if>
<if test="isDelete != null">is_delete = #{isDelete},</if>
<if test="created != null">created = #{created},</if>
<if test="modified != null">modified = #{modified},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="goodsName != null and goodsName != ''">goods_name = #{goodsName},</if>
<if test="goodsModel != null">goods_model = #{goodsModel},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteGoodsMessagesById" parameterType="Long">
delete from GOODS_MESSAGES where id = #{id}
</delete>
<delete id="deleteGoodsMessagesByIds" parameterType="String">
delete from GOODS_MESSAGES where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -0,0 +1,110 @@
package com.sf.system.domain;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.sf.common.annotation.Excel;
import com.sf.common.core.domain.BaseEntity;
/**
* 会员对象 User_member
*
* @author ztzh
* @date 2024-04-16
*/
public class UserMember extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 用户会员等级0普通会员 1订阅会员 2月度会员 */
@Excel(name = "用户会员等级", readConverterExp = "0=普通会员,1=订阅会员,2=月度会员")
private Integer memberLevel;
/** 会员等级为订阅会员时的订阅状态未订阅就是取消了0未订阅 1已订阅 */
@Excel(name = "会员等级为订阅会员时的订阅状态,未订阅就是取消了", readConverterExp = "0=未订阅,1=已订阅")
private Integer subscriptionStatus;
/** 关联的用户id */
@Excel(name = "关联的用户id")
private Long userId;
/** 积分 */
@Excel(name = "积分")
private Long integration;
/** 过期时间 */
private Date expirationTime;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setMemberLevel(Integer memberLevel)
{
this.memberLevel = memberLevel;
}
public Integer getMemberLevel()
{
return memberLevel;
}
public void setSubscriptionStatus(Integer subscriptionStatus)
{
this.subscriptionStatus = subscriptionStatus;
}
public Integer getSubscriptionStatus()
{
return subscriptionStatus;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setIntegration(Long integration)
{
this.integration = integration;
}
public Long getIntegration()
{
return integration;
}
public void setExpirationTime(Date expirationTime)
{
this.expirationTime = expirationTime;
}
public Date getExpirationTime()
{
return expirationTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("memberLevel", getMemberLevel())
.append("subscriptionStatus", getSubscriptionStatus())
.append("userId", getUserId())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.append("integration", getIntegration())
.append("expirationTime", getExpirationTime())
.toString();
}
}

View File

@ -0,0 +1,63 @@
package com.sf.system.mapper;
import java.util.List;
import com.sf.system.domain.UserMember;
/**
* 会员Mapper接口
*
* @author ztzh
* @date 2024-04-16
*/
public interface UserMemberMapper
{
/**
* 查询会员
*
* @param id 会员主键
* @return 会员
*/
public UserMember selectUserMemberById(Long id);
/**
* 查询会员列表
*
* @param userMember 会员
* @return 会员集合
*/
public List<UserMember> selectUserMemberList(UserMember userMember);
/**
* 新增会员
*
* @param userMember 会员
* @return 结果
*/
public int insertUserMember(UserMember userMember);
/**
* 修改会员
*
* @param userMember 会员
* @return 结果
*/
public int updateUserMember(UserMember userMember);
/**
* 删除会员
*
* @param id 会员主键
* @return 结果
*/
public int deleteUserMemberById(Long id);
/**
* 批量删除会员
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteUserMemberByIds(Long[] ids);
UserMember selectUserMemberByUserId(Long userId);
}

View File

@ -0,0 +1,63 @@
package com.sf.system.service;
import java.util.List;
import com.sf.system.domain.UserMember;
/**
* 会员Service接口
*
* @author ztzh
* @date 2024-04-16
*/
public interface IUserMemberService
{
/**
* 查询会员
*
* @param id 会员主键
* @return 会员
*/
public UserMember selectUserMemberById(Long id);
/**
* 查询会员列表
*
* @param userMember 会员
* @return 会员集合
*/
public List<UserMember> selectUserMemberList(UserMember userMember);
/**
* 新增会员
*
* @param userMember 会员
* @return 结果
*/
public int insertUserMember(UserMember userMember);
/**
* 修改会员
*
* @param userMember 会员
* @return 结果
*/
public int updateUserMember(UserMember userMember);
/**
* 批量删除会员
*
* @param ids 需要删除的会员主键集合
* @return 结果
*/
public int deleteUserMemberByIds(Long[] ids);
/**
* 删除会员信息
*
* @param id 会员主键
* @return 结果
*/
public int deleteUserMemberById(Long id);
UserMember selectUserMemberByUserId(Long userId);
}

View File

@ -0,0 +1,101 @@
package com.sf.system.service.impl;
import java.util.List;
import com.sf.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.sf.system.mapper.UserMemberMapper;
import com.sf.system.domain.UserMember;
import com.sf.system.service.IUserMemberService;
/**
* 会员Service业务层处理
*
* @author ztzh
* @date 2024-04-16
*/
@Service
public class UserMemberServiceImpl implements IUserMemberService
{
@Autowired
private UserMemberMapper userMemberMapper;
/**
* 查询会员
*
* @param id 会员主键
* @return 会员
*/
@Override
public UserMember selectUserMemberById(Long id)
{
return userMemberMapper.selectUserMemberById(id);
}
/**
* 查询会员列表
*
* @param userMember 会员
* @return 会员
*/
@Override
public List<UserMember> selectUserMemberList(UserMember userMember)
{
return userMemberMapper.selectUserMemberList(userMember);
}
/**
* 新增会员
*
* @param userMember 会员
* @return 结果
*/
@Override
public int insertUserMember(UserMember userMember)
{
userMember.setCreateTime(DateUtils.getNowDate());
return userMemberMapper.insertUserMember(userMember);
}
/**
* 修改会员
*
* @param userMember 会员
* @return 结果
*/
@Override
public int updateUserMember(UserMember userMember)
{
userMember.setUpdateTime(DateUtils.getNowDate());
return userMemberMapper.updateUserMember(userMember);
}
/**
* 批量删除会员
*
* @param ids 需要删除的会员主键
* @return 结果
*/
@Override
public int deleteUserMemberByIds(Long[] ids)
{
return userMemberMapper.deleteUserMemberByIds(ids);
}
/**
* 删除会员信息
*
* @param id 会员主键
* @return 结果
*/
@Override
public int deleteUserMemberById(Long id)
{
return userMemberMapper.deleteUserMemberById(id);
}
@Override
public UserMember selectUserMemberByUserId(Long userId) {
return userMemberMapper.selectUserMemberByUserId(userId);
}
}

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sf.system.mapper.UserMemberMapper">
<resultMap type="UserMember" id="UserMemberResult">
<result property="id" column="id" />
<result property="memberLevel" column="member_level" />
<result property="subscriptionStatus" column="subscription_status" />
<result property="userId" column="user_id" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="integration" column="integration" />
<result property="expirationTime" column="expiration_time" />
</resultMap>
<sql id="selectUserMemberVo">
select id, member_level, subscription_status, user_id, create_time, update_time, integration, expiration_time from User_member
</sql>
<select id="selectUserMemberList" parameterType="UserMember" resultMap="UserMemberResult">
<include refid="selectUserMemberVo"/>
<where>
<if test="memberLevel != null "> and member_level = #{memberLevel}</if>
<if test="subscriptionStatus != null "> and subscription_status = #{subscriptionStatus}</if>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="integration != null "> and integration = #{integration}</if>
<if test="expirationTime != null "> and expiration_time = #{expirationTime}</if>
</where>
</select>
<select id="selectUserMemberById" parameterType="Long" resultMap="UserMemberResult">
<include refid="selectUserMemberVo"/>
where id = #{id}
</select>
<select id="selectUserMemberByUserId" resultMap="UserMemberResult">
<include refid="selectUserMemberVo"/>
where user_id = #{userId}
</select>
<insert id="insertUserMember" parameterType="UserMember" useGeneratedKeys="true" keyProperty="id">
insert into User_member
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="memberLevel != null">member_level,</if>
<if test="subscriptionStatus != null">subscription_status,</if>
<if test="userId != null">user_id,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
<if test="integration != null">integration,</if>
<if test="expirationTime != null">expiration_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="memberLevel != null">#{memberLevel},</if>
<if test="subscriptionStatus != null">#{subscriptionStatus},</if>
<if test="userId != null">#{userId},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="integration != null">#{integration},</if>
<if test="expirationTime != null">#{expirationTime},</if>
</trim>
</insert>
<update id="updateUserMember" parameterType="UserMember">
update User_member
<trim prefix="SET" suffixOverrides=",">
<if test="memberLevel != null">member_level = #{memberLevel},</if>
<if test="subscriptionStatus != null">subscription_status = #{subscriptionStatus},</if>
<if test="userId != null">user_id = #{userId},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
<if test="integration != null">integration = #{integration},</if>
<if test="expirationTime != null">expiration_time = #{expirationTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteUserMemberById" parameterType="Long">
delete from User_member where id = #{id}
</delete>
<delete id="deleteUserMemberByIds" parameterType="String">
delete from User_member where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -1,44 +1,44 @@
import request from '@/utils/request'
// 查询商品信息列表
export function listGoods(query) {
return request({
url: '/goods/goods/list',
method: 'get',
params: query
})
}
// 查询商品信息详细
export function getGoods(id) {
return request({
url: '/goods/goods/' + id,
method: 'get'
})
}
// 新增商品信息
export function addGoods(data) {
return request({
url: '/goods/goods',
method: 'post',
data: data
})
}
// 修改商品信息
export function updateGoods(data) {
return request({
url: '/goods/goods',
method: 'put',
data: data
})
}
// 删除商品信息
export function delGoods(id) {
return request({
url: '/goods/goods/' + id,
method: 'delete'
})
}
import request from '@/utils/request'
// 查询商品信息列表
export function listGoods(query) {
return request({
url: '/service/goods/list',
method: 'get',
params: query
})
}
// 查询商品信息详细
export function getGoods(id) {
return request({
url: '/service/goods/' + id,
method: 'get'
})
}
// 新增商品信息
export function addGoods(data) {
return request({
url: '/service/goods',
method: 'post',
data: data
})
}
// 修改商品信息
export function updateGoods(data) {
return request({
url: '/service/goods',
method: 'put',
data: data
})
}
// 删除商品信息
export function delGoods(id) {
return request({
url: '/service/goods/' + id,
method: 'delete'
})
}

View File

@ -1,348 +1,405 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品编号" prop="goodsCode">
<el-input
v-model="queryParams.goodsCode"
placeholder="请输入商品编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品标题" prop="productTitle">
<el-input
v-model="queryParams.productTitle"
placeholder="请输入商品标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品类型" prop="goodsType">
<el-select v-model="queryParams.goodsType" placeholder="请选择商品类型" clearable>
<el-option
v-for="dict in dict.type.goods_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['goods:goods:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['goods:goods:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['goods:goods:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['goods:goods:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="goodsList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="商品id" align="center" prop="id" />
<el-table-column label="商品编号" align="center" prop="goodsCode" />
<el-table-column label="商品标题" align="center" prop="productTitle" />
<el-table-column label="商品图片" align="center" prop="productPicture" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.productPicture" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="originalPrice" />
<el-table-column label="商品描述" align="center" prop="productDesc" />
<el-table-column label="排序" align="center" prop="orderNum" />
<el-table-column label="商品类型" align="center" prop="goodsType">
<template slot-scope="scope">
<dict-tag :options="dict.type.goods_type" :value="scope.row.goodsType"/>
</template>
</el-table-column>
<el-table-column label="商品规格" align="center" prop="goodsSpec" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['goods:goods:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['goods:goods:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改商品信息对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="商品编号" prop="goodsCode">
<el-input v-model="form.goodsCode" placeholder="请输入商品编号" />
</el-form-item>
<el-form-item label="商品标题" prop="productTitle">
<el-input v-model="form.productTitle" placeholder="请输入商品标题" />
</el-form-item>
<el-form-item label="商品图片" prop="productPicture">
<image-upload v-model="form.productPicture"/>
</el-form-item>
<el-form-item label="原价格" prop="originalPrice">
<el-input v-model="form.originalPrice" placeholder="请输入原价格" />
</el-form-item>
<el-form-item label="商品描述" prop="productDesc">
<el-input v-model="form.productDesc" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="排序" prop="orderNum">
<el-input v-model="form.orderNum" placeholder="请输入排序" />
</el-form-item>
<el-form-item label="商品类型" prop="goodsType">
<el-radio-group v-model="form.goodsType">
<el-radio
v-for="dict in dict.type.goods_type"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="商品规格" prop="goodsSpec">
<el-input v-model="form.goodsSpec" placeholder="请输入商品规格" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listGoods, getGoods, delGoods, addGoods, updateGoods } from "@/api/goods/goods";
export default {
name: "Goods",
dicts: ['sys_notice_status', 'goods_type'],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
goodsList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
goodsCode: null,
productTitle: null,
goodsType: null,
},
//
form: {},
//
rules: {
goodsCode: [
{ required: true, message: "商品编号不能为空", trigger: "blur" }
],
productTitle: [
{ required: true, message: "商品标题不能为空", trigger: "blur" }
],
productPicture: [
{ required: true, message: "商品图片不能为空", trigger: "blur" }
],
originalPrice: [
{ required: true, message: "原价格不能为空", trigger: "blur" }
],
orderNum: [
{ required: true, message: "排序不能为空", trigger: "blur" }
],
createTime: [
{ required: true, message: "创建时间不能为空", trigger: "blur" }
],
goodsType: [
{ required: true, message: "商品类型不能为空", trigger: "change" }
],
goodsSpec: [
{ required: true, message: "商品规格不能为空", trigger: "blur" }
]
}
};
},
created() {
this.getList();
},
methods: {
/** 查询商品信息列表 */
getList() {
this.loading = true;
listGoods(this.queryParams).then(response => {
this.goodsList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
goodsCode: null,
stockId: null,
reviewStatus: null,
productTitle: null,
productPicture: null,
originalPrice: null,
productDesc: null,
orderNum: null,
isDelete: null,
created: null,
modified: null,
createTime: null,
updateTime: null,
goodsType: null,
goodsSpec: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加商品信息";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getGoods(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改商品信息";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
let data = Object.assign({},this.form)
delete data.explain
updateGoods(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addGoods(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除商品信息编号为"' + ids + '"的数据项?').then(function() {
return delGoods(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('goods/goods/export', {
...this.queryParams
}, `goods_${new Date().getTime()}.xlsx`)
}
}
};
</script>
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品编号" prop="goodsCode">
<el-input
v-model="queryParams.goodsCode"
placeholder="请输入商品编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="审核状态" prop="reviewStatus">
<el-select v-model="queryParams.reviewStatus" placeholder="请选择审核状态" clearable>
<el-option
v-for="dict in dict.type.goods_review_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="商品标题" prop="productTitle">
<el-input
v-model="queryParams.productTitle"
placeholder="请输入商品标题"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品原价" prop="originalPrice">
<el-input
v-model="queryParams.originalPrice"
placeholder="请输入商品原价"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品类型" prop="goodsType">
<el-select v-model="queryParams.goodsType" placeholder="请选择商品类型" clearable>
<el-option
v-for="dict in dict.type.goods_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="商品规格" prop="goodsSpec">
<el-input
v-model="queryParams.goodsSpec"
placeholder="请输入商品规格"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品型号" prop="goodsModel">
<el-input
v-model="queryParams.goodsModel"
placeholder="请输入商品型号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['service:goods:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['service:goods:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['service:goods:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['service:goods:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="goodsList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="商品编号" align="center" prop="goodsCode" />
<el-table-column label="审核状态" align="center" prop="reviewStatus">
<template slot-scope="scope">
<dict-tag :options="dict.type.goods_review_status" :value="scope.row.reviewStatus"/>
</template>
</el-table-column>
<el-table-column label="商品标题" align="center" prop="productTitle" />
<el-table-column label="商品图片" align="center" prop="productPicture" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.productPicture" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="商品原价" align="center" prop="originalPrice" />
<el-table-column label="商品描述" align="center" prop="productDesc" />
<el-table-column label="商品类型" align="center" prop="goodsType">
<template slot-scope="scope">
<dict-tag :options="dict.type.goods_type" :value="scope.row.goodsType"/>
</template>
</el-table-column>
<el-table-column label="商品规格" align="center" prop="goodsSpec" />
<el-table-column label="商品型号" align="center" prop="goodsModel" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['service:goods:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['service:goods:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改商品信息对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="商品编号" prop="goodsCode">
<el-input v-model="form.goodsCode" placeholder="请输入商品编号" />
</el-form-item>
<el-form-item label="审核状态" prop="reviewStatus">
<el-radio-group v-model="form.reviewStatus">
<el-radio
v-for="dict in dict.type.goods_review_status"
:key="dict.value"
:label="parseInt(dict.value)"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="商品标题" prop="productTitle">
<el-input v-model="form.productTitle" placeholder="请输入商品标题" />
</el-form-item>
<el-form-item label="商品图片" prop="productPicture">
<image-upload v-model="form.productPicture"/>
</el-form-item>
<el-form-item label="商品原价" prop="originalPrice">
<el-input v-model="form.originalPrice" placeholder="请输入商品原价" />
</el-form-item>
<el-form-item label="商品描述" prop="productDesc">
<el-input v-model="form.productDesc" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="商品类型" prop="goodsType">
<el-radio-group v-model="form.goodsType">
<el-radio
v-for="dict in dict.type.goods_type"
:key="dict.value"
:label="parseInt(dict.value)"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="商品规格" prop="goodsSpec">
<el-input v-model="form.goodsSpec" placeholder="请输入商品规格" />
</el-form-item>
<el-form-item label="排序" prop="orderNum">
<el-input v-model="form.orderNum" placeholder="请输入排序" />
</el-form-item>
<el-form-item label="商品型号" prop="goodsModel">
<el-input v-model="form.goodsModel" placeholder="请输入商品型号" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listGoods, getGoods, delGoods, addGoods, updateGoods } from "@/api/service/goods";
export default {
name: "Goods",
dicts: ['goods_review_status', 'goods_type'],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
goodsList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
goodsCode: null,
reviewStatus: null,
productTitle: null,
originalPrice: null,
goodsType: null,
goodsSpec: null,
goodsModel: null
},
//
form: {},
//
rules: {
goodsCode: [
{ required: true, message: "商品编号不能为空", trigger: "blur" }
],
reviewStatus: [
{ required: true, message: "审核状态不能为空", trigger: "change" }
],
productTitle: [
{ required: true, message: "商品标题不能为空", trigger: "blur" }
],
productPicture: [
{ required: true, message: "商品图片不能为空", trigger: "blur" }
],
originalPrice: [
{ required: true, message: "商品原价不能为空", trigger: "blur" }
],
goodsType: [
{ required: true, message: "商品类型不能为空", trigger: "change" }
],
goodsSpec: [
{ required: true, message: "商品规格不能为空", trigger: "blur" }
],
goodsModel: [
{ required: true, message: "商品型号不能为空", trigger: "blur" }
]
}
};
},
created() {
this.getList();
},
methods: {
/** 查询商品信息列表 */
getList() {
this.loading = true;
listGoods(this.queryParams).then(response => {
this.goodsList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
goodsCode: null,
stockId: null,
reviewStatus: null,
productTitle: null,
productPicture: null,
originalPrice: null,
productDesc: null,
goodsType: null,
goodsSpec: null,
orderNum: null,
isDelete: null,
created: null,
modified: null,
createTime: null,
updateTime: null,
goodsName: null,
goodsModel: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加商品信息";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getGoods(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改商品信息";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
let data = Object.assign({},this.form)
delete data.explain
updateGoods(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addGoods(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除商品信息编号为"' + ids + '"的数据项?').then(function() {
return delGoods(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('service/goods/export', {
...this.queryParams
}, `goods_${new Date().getTime()}.xlsx`)
}
}
};
</script>