diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e784929 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM openjdk:8 +# 复制jar文件到路径 +COPY ./sf-admin/target/sf-admin.jar /usr/local/sf-admin.jar +RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone +# 指定路径 +WORKDIR /usr/local + +ENTRYPOINT ["java","-jar","sf-admin.jar"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index f919233..e85953e 100644 --- a/pom.xml +++ b/pom.xml @@ -146,12 +146,6 @@ sf-file ${sf.version} - - - com.smarterFramework - sf-service - ${sf.version} - @@ -167,6 +161,30 @@ ${sf.version} + + + com.smarterFramework + sf-oauth + ${sf.version} + + + + com.smarterFramework + sf-order + ${sf.version} + + + + com.smarterFramework + sf-payment + ${sf.version} + + + + com.smarterFramework + sf-service + ${sf.version} + com.smarterFramework @@ -211,6 +229,10 @@ sf-file sf-common sf-apijson + sf-oauth + sf-payment + sf-service + sf-order sf-vertx sf-vertx-api sf-service diff --git a/sf-admin/pom.xml b/sf-admin/pom.xml index 785310e..4a09d6e 100644 --- a/sf-admin/pom.xml +++ b/sf-admin/pom.xml @@ -61,6 +61,21 @@ sf-file + + com.smarterFramework + sf-oauth + + + + com.smarterFramework + sf-order + + + + com.smarterFramework + sf-payment + + com.smarterFramework diff --git a/sf-admin/src/main/java/com/sf/index/controller/ApplyListInfoController.java b/sf-admin/src/main/java/com/sf/index/controller/ApplyListInfoController.java new file mode 100644 index 0000000..bfc6939 --- /dev/null +++ b/sf-admin/src/main/java/com/sf/index/controller/ApplyListInfoController.java @@ -0,0 +1,107 @@ +package com.sf.index.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +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.index.domain.ApplyListInfo; +import com.sf.index.service.IApplyListInfoService; +import com.sf.common.utils.poi.ExcelUtil; +import com.sf.common.core.page.TableDataInfo; + +/** + * 应用列Controller + * + * @author ztzh + * @date 2024-04-11 + */ +@RestController +@RequestMapping("/index/list") +public class ApplyListInfoController extends BaseController +{ + @Autowired + private IApplyListInfoService applyListInfoService; + + /** + * 查询应用列列表 + */ + @PreAuthorize("@ss.hasPermi('index:list:list')") + @GetMapping("/list") + public TableDataInfo list(ApplyListInfo applyListInfo) + { + startPage(); + List list = applyListInfoService.selectApplyListInfoList(applyListInfo); + return getDataTable(list); + } + + /** + * 导出应用列列表 + */ + @PreAuthorize("@ss.hasPermi('index:list:export')") + @Log(title = "应用列", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ApplyListInfo applyListInfo) + { + List list = applyListInfoService.selectApplyListInfoList(applyListInfo); + ExcelUtil util = new ExcelUtil(ApplyListInfo.class); + util.exportExcel(response, list, "应用列数据"); + } + + /** + * 获取应用列详细信息 + */ + @PreAuthorize("@ss.hasPermi('index:list:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(applyListInfoService.selectApplyListInfoById(id)); + } + + /** + * 新增应用列 + */ + @PreAuthorize("@ss.hasPermi('index:list:add')") + @Log(title = "应用列", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody ApplyListInfo applyListInfo) + { + applyListInfo.setCreated(getUsername()); + applyListInfo.setModified(getUsername()); + return toAjax(applyListInfoService.insertApplyListInfo(applyListInfo)); + } + + /** + * 修改应用列 + */ + @PreAuthorize("@ss.hasPermi('index:list:edit')") + @Log(title = "应用列", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ApplyListInfo applyListInfo) + { + applyListInfo.setModified(getUsername()); + return toAjax(applyListInfoService.updateApplyListInfo(applyListInfo)); + } + + /** + * 删除应用列 + */ + @PreAuthorize("@ss.hasPermi('index:list:remove')") + @Log(title = "应用列", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(applyListInfoService.deleteApplyListInfoByIds(ids)); + } +} diff --git a/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java b/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java new file mode 100644 index 0000000..47eaa62 --- /dev/null +++ b/sf-admin/src/main/java/com/sf/index/domain/ApplyListInfo.java @@ -0,0 +1,153 @@ +package com.sf.index.domain; + +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; + +/** + * 应用列对象 APPLY_LIST_INFO + * + * @author ztzh + * @date 2024-04-11 + */ +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:删除 + */ + private Long isDelete; + + /** + * 创建人 + */ + private String created; + + /** + * 更新人 + */ + private String modified; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getAppName() { + return appName; + } + + public void setAppDesc(String appDesc) { + this.appDesc = appDesc; + } + + public String getAppDesc() { + return appDesc; + } + + public void setPicture(String picture) { + this.picture = picture; + } + + public String getPicture() { + return picture; + } + + public void setOrderNum(Long orderNum) { + this.orderNum = orderNum; + } + + public Long getOrderNum() { + return orderNum; + } + + public void setIsDelete(Long isDelete) { + this.isDelete = isDelete; + } + + public Long getIsDelete() { + return isDelete; + } + + public void setCreated(String created) { + this.created = created; + } + + public String getCreated() { + return created; + } + + public void setModified(String modified) { + this.modified = modified; + } + + 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("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(); + } + +} diff --git a/sf-admin/src/main/java/com/sf/index/mapper/ApplyListInfoMapper.java b/sf-admin/src/main/java/com/sf/index/mapper/ApplyListInfoMapper.java new file mode 100644 index 0000000..f7a1911 --- /dev/null +++ b/sf-admin/src/main/java/com/sf/index/mapper/ApplyListInfoMapper.java @@ -0,0 +1,61 @@ +package com.sf.index.mapper; + +import java.util.List; +import com.sf.index.domain.ApplyListInfo; + +/** + * 应用列Mapper接口 + * + * @author ztzh + * @date 2024-04-11 + */ +public interface ApplyListInfoMapper +{ + /** + * 查询应用列 + * + * @param id 应用列主键 + * @return 应用列 + */ + public ApplyListInfo selectApplyListInfoById(Long id); + + /** + * 查询应用列列表 + * + * @param applyListInfo 应用列 + * @return 应用列集合 + */ + public List selectApplyListInfoList(ApplyListInfo applyListInfo); + + /** + * 新增应用列 + * + * @param applyListInfo 应用列 + * @return 结果 + */ + public int insertApplyListInfo(ApplyListInfo applyListInfo); + + /** + * 修改应用列 + * + * @param applyListInfo 应用列 + * @return 结果 + */ + public int updateApplyListInfo(ApplyListInfo applyListInfo); + + /** + * 删除应用列 + * + * @param id 应用列主键 + * @return 结果 + */ + public int deleteApplyListInfoById(Long id); + + /** + * 批量删除应用列 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteApplyListInfoByIds(Long[] ids); +} diff --git a/sf-admin/src/main/java/com/sf/index/service/IApplyListInfoService.java b/sf-admin/src/main/java/com/sf/index/service/IApplyListInfoService.java new file mode 100644 index 0000000..ff17ecd --- /dev/null +++ b/sf-admin/src/main/java/com/sf/index/service/IApplyListInfoService.java @@ -0,0 +1,61 @@ +package com.sf.index.service; + +import java.util.List; +import com.sf.index.domain.ApplyListInfo; + +/** + * 应用列Service接口 + * + * @author ztzh + * @date 2024-04-11 + */ +public interface IApplyListInfoService +{ + /** + * 查询应用列 + * + * @param id 应用列主键 + * @return 应用列 + */ + public ApplyListInfo selectApplyListInfoById(Long id); + + /** + * 查询应用列列表 + * + * @param applyListInfo 应用列 + * @return 应用列集合 + */ + public List selectApplyListInfoList(ApplyListInfo applyListInfo); + + /** + * 新增应用列 + * + * @param applyListInfo 应用列 + * @return 结果 + */ + public int insertApplyListInfo(ApplyListInfo applyListInfo); + + /** + * 修改应用列 + * + * @param applyListInfo 应用列 + * @return 结果 + */ + public int updateApplyListInfo(ApplyListInfo applyListInfo); + + /** + * 批量删除应用列 + * + * @param ids 需要删除的应用列主键集合 + * @return 结果 + */ + public int deleteApplyListInfoByIds(Long[] ids); + + /** + * 删除应用列信息 + * + * @param id 应用列主键 + * @return 结果 + */ + public int deleteApplyListInfoById(Long id); +} diff --git a/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java b/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java new file mode 100644 index 0000000..c77d3e8 --- /dev/null +++ b/sf-admin/src/main/java/com/sf/index/service/impl/ApplyListInfoServiceImpl.java @@ -0,0 +1,98 @@ +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; +import com.sf.index.domain.ApplyListInfo; +import com.sf.index.service.IApplyListInfoService; + +/** + * 应用列Service业务层处理 + * + * @author ztzh + * @date 2024-04-11 + */ +@Service +public class ApplyListInfoServiceImpl implements IApplyListInfoService +{ + @Autowired + private ApplyListInfoMapper applyListInfoMapper; + + /** + * 查询应用列 + * + * @param id 应用列主键 + * @return 应用列 + */ + @Override + public ApplyListInfo selectApplyListInfoById(Long id) + { + return applyListInfoMapper.selectApplyListInfoById(id); + } + + /** + * 查询应用列列表 + * + * @param applyListInfo 应用列 + * @return 应用列 + */ + @Override + public List selectApplyListInfoList(ApplyListInfo applyListInfo) + { + return applyListInfoMapper.selectApplyListInfoList(applyListInfo); + } + + /** + * 新增应用列 + * + * @param applyListInfo 应用列 + * @return 结果 + */ + @Override + public int insertApplyListInfo(ApplyListInfo applyListInfo) + { + applyListInfo.setCreateTime(DateUtils.getNowDate()); + applyListInfo.setAppCode(IdUtils.randomTime("ZT")); + return applyListInfoMapper.insertApplyListInfo(applyListInfo); + } + + /** + * 修改应用列 + * + * @param applyListInfo 应用列 + * @return 结果 + */ + @Override + public int updateApplyListInfo(ApplyListInfo applyListInfo) + { + applyListInfo.setUpdateTime(DateUtils.getNowDate()); + return applyListInfoMapper.updateApplyListInfo(applyListInfo); + } + + /** + * 批量删除应用列 + * + * @param ids 需要删除的应用列主键 + * @return 结果 + */ + @Override + public int deleteApplyListInfoByIds(Long[] ids) + { + return applyListInfoMapper.deleteApplyListInfoByIds(ids); + } + + /** + * 删除应用列信息 + * + * @param id 应用列主键 + * @return 结果 + */ + @Override + public int deleteApplyListInfoById(Long id) + { + return applyListInfoMapper.deleteApplyListInfoById(id); + } +} diff --git a/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java b/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java index 470f95f..ab7e914 100644 --- a/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java +++ b/sf-admin/src/main/java/com/sf/web/controller/common/CommonController.java @@ -88,6 +88,7 @@ public class CommonController ajax.put("fileName", fileName); ajax.put("newFileName", FileUtils.getName(fileName)); ajax.put("originalFilename", file.getOriginalFilename()); + ajax.put("fileSize", file.getSize()); return ajax; } catch (Exception e) @@ -95,7 +96,6 @@ public class CommonController return AjaxResult.error(e.getMessage()); } } - /** * 通用上传请求(多个) */ diff --git a/sf-admin/src/main/java/com/sf/web/controller/system/SysLoginController.java b/sf-admin/src/main/java/com/sf/web/controller/system/SysLoginController.java index cc05d23..27c4781 100644 --- a/sf-admin/src/main/java/com/sf/web/controller/system/SysLoginController.java +++ b/sf-admin/src/main/java/com/sf/web/controller/system/SysLoginController.java @@ -49,8 +49,8 @@ public class SysLoginController { AjaxResult ajax = AjaxResult.success(); // 生成令牌 - String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), - loginBody.getUuid(), session); + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), + loginBody.getCode(),loginBody.getUuid(), session); ajax.put(Constants.TOKEN, token); return ajax; } diff --git a/sf-admin/src/main/java/com/sf/web/controller/system/SysUserController.java b/sf-admin/src/main/java/com/sf/web/controller/system/SysUserController.java index b9d689f..e5e481b 100644 --- a/sf-admin/src/main/java/com/sf/web/controller/system/SysUserController.java +++ b/sf-admin/src/main/java/com/sf/web/controller/system/SysUserController.java @@ -35,7 +35,8 @@ import com.sf.system.service.ISysUserService; /** * 用户信息 - * + * + * * @author ztzh */ @RestController diff --git a/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java b/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java new file mode 100644 index 0000000..294c62a --- /dev/null +++ b/sf-admin/src/main/java/com/sf/web/controller/system/UserMemberController.java @@ -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 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 list = userMemberService.selectUserMemberList(userMember); + ExcelUtil util = new ExcelUtil(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)); + } +} diff --git a/sf-admin/src/main/resources/application-druid.yml b/sf-admin/src/main/resources/application-druid.yml index c0a4cd2..4dcf478 100644 --- a/sf-admin/src/main/resources/application-druid.yml +++ b/sf-admin/src/main/resources/application-druid.yml @@ -6,9 +6,9 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://192.168.1.23:22001/sac?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://47.108.66.163:3306/sac_competition?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root - password: abc&^321 + password: 82^hsgGHbs^2 # 从库数据源 slave: # 从数据源开关/默认关闭 diff --git a/sf-admin/src/main/resources/application.yml b/sf-admin/src/main/resources/application.yml index 7ec7689..7a95910 100644 --- a/sf-admin/src/main/resources/application.yml +++ b/sf-admin/src/main/resources/application.yml @@ -59,9 +59,9 @@ spring: servlet: multipart: # 单个文件大小 - max-file-size: 10MB + max-file-size: 100MB # 设置总上传的文件大小 - max-request-size: 20MB + max-request-size: 200MB # 服务模块 devtools: restart: diff --git a/sf-admin/src/main/resources/file/server.crt b/sf-admin/src/main/resources/file/server.crt new file mode 100644 index 0000000..297fd5c --- /dev/null +++ b/sf-admin/src/main/resources/file/server.crt @@ -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----- diff --git a/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml b/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml new file mode 100644 index 0000000..6ab3a1f --- /dev/null +++ b/sf-admin/src/main/resources/mapper/index/ApplyListInfoMapper.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + select id, app_code, app_name, app_desc, picture, order_num, is_delete, created, modified, create_time, update_time from APPLY_LIST_INFO + + + + + + + + insert into APPLY_LIST_INFO + + app_code, + app_name, + app_desc, + picture, + order_num, + is_delete, + created, + modified, + create_time, + update_time, + + + #{appCode}, + #{appName}, + #{appDesc}, + #{picture}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + + + + + update APPLY_LIST_INFO + + app_code = #{appCode}, + app_name = #{appName}, + app_desc = #{appDesc}, + picture = #{picture}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from APPLY_LIST_INFO where id = #{id} + + + + delete from APPLY_LIST_INFO where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-common/pom.xml b/sf-common/pom.xml index b6c4893..e56cb41 100644 --- a/sf-common/pom.xml +++ b/sf-common/pom.xml @@ -15,6 +15,17 @@ + + + net.glxn.qrgen + javase + 2.0 + + + commons-fileupload + commons-fileupload + 1.4 + org.springframework diff --git a/sf-common/src/main/java/com/sf/common/enums/RequestHeaderEnums.java b/sf-common/src/main/java/com/sf/common/enums/RequestHeaderEnums.java new file mode 100644 index 0000000..eed566a --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/enums/RequestHeaderEnums.java @@ -0,0 +1,47 @@ +package com.sf.common.enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * header参数 + * + * @author ztzh + * + */ +public enum RequestHeaderEnums +{ + /** + * 通知完成指令,用于客户端收到通知消息的确认 + */ + APP_CODE("appCode"), + + /** + * 通知完成指令,用于客户端收到通知消息的确认 Authorization + */ + AUTHORIZATION("Authorization"), + ; + + private static Map map = new HashMap<>(); + + static { + for (RequestHeaderEnums type : RequestHeaderEnums.values()) { + map.put(type.code, type); + } + } + + + private String code; + + RequestHeaderEnums(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public static final RequestHeaderEnums getByCode(String code) { + return map.get(code); + } +} diff --git a/sf-common/src/main/java/com/sf/common/utils/SnowflakeIdWorker.java b/sf-common/src/main/java/com/sf/common/utils/SnowflakeIdWorker.java new file mode 100644 index 0000000..31257ae --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/SnowflakeIdWorker.java @@ -0,0 +1,152 @@ +package com.sf.common.utils; + + +import org.springframework.stereotype.Component; + +/** + * Tiwtter.SnowFlake的结构如下(每部分用-分开):
+ * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
+ * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
+ * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) + * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
+ * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
+ * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
+ * 加起来刚好64位,为一个Long型。
+ * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 + * @author yswjme@163.com + * @date : + * + */ +@Component +public class SnowflakeIdWorker { + + /** 开始时间截 (2018-01-01) ,不可修改 */ + private final long twepoch = 1420041600000L; + + /** 机器id所占的位数 */ + private final long workerIdBits = 5L; + + /** 数据标识id所占的位数 */ + private final long datacenterIdBits = 5L; + + /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** 支持的最大数据标识id,结果是31 */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** 序列在id中占的位数 */ + private final long sequenceBits = 12L; + + /** 机器ID向左移12位 */ + private final long workerIdShift = sequenceBits; + + /** 数据标识id向左移17位(12+5) */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** 时间截向左移22位(5+5+12) */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** 工作机器ID(0~31) */ + private long workerId; + + /** 数据中心ID(0~31) */ + private long datacenterId; + + /** 毫秒内序列(0~4095) */ + private long sequence = 0L; + + /** 上次生成ID的时间截 */ + private long lastTimestamp = -1L; + + /** + * 构造函数 + * + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public SnowflakeIdWorker(long workerId, long datacenterId) { + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format( + "worker Id can't be greater than %d or less than 0", + maxWorkerId)); + } + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format( + "datacenter Id can't be greater than %d or less than 0", + maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + public SnowflakeIdWorker() { + }; + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException( + String.format( + "Clock moved backwards. Refusing to generate id for %d milliseconds", + lastTimestamp - timestamp)); + } + + // 如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + // 毫秒内序列溢出 + if (sequence == 0) { + // 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + }else {// 时间戳改变,毫秒内序列重置 + sequence = 0L; + } + + // 上次生成ID的时间截 + lastTimestamp = timestamp; + + // 移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) // + | (datacenterId << datacenterIdShift) // + | (workerId << workerIdShift) // + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp + * 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + + +} diff --git a/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java b/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java index c53b282..6ec8403 100644 --- a/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java +++ b/sf-common/src/main/java/com/sf/common/utils/file/FileUtils.java @@ -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,55 @@ 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; + } + public static MultipartFile fileToMultipartFile(File file) throws IOException { + try { + FileInputStream fis = new FileInputStream(file); + ByteArrayOutputStream baos = new ByteArrayOutputStream(fis.available()); + byte[] bytes = new byte[fis.available()]; + int temp; + while ((temp = fis.read(bytes)) != -1) { + baos.write(bytes, 0, temp); + } + + // 文件输出流 + FileItemFactory factory = new DiskFileItemFactory(16, null); + FileItem item = factory.createItem("image", "image/png", false, "123.png"); + OutputStream os = item.getOutputStream(); + os.write(bytes); + fis.close(); + baos.close(); + os.flush(); + + // MultipartFile + MultipartFile multipartFile = new CommonsMultipartFile(item); + return multipartFile; + } catch (Exception e) { + System.err.println("二维码转换异常"); + } + return null; + } } diff --git a/sf-common/src/main/java/com/sf/common/utils/file/QRCodeUtils.java b/sf-common/src/main/java/com/sf/common/utils/file/QRCodeUtils.java new file mode 100644 index 0000000..b1745e8 --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/file/QRCodeUtils.java @@ -0,0 +1,18 @@ +package com.sf.common.utils.file; + + +import net.glxn.qrgen.core.image.ImageType; +import net.glxn.qrgen.javase.QRCode; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; + +public class QRCodeUtils { + + public static MultipartFile generateQRCode(String data) throws IOException { + File qrCodeFile = QRCode.from(data).to(ImageType.PNG).file(); + return FileUtils.fileToMultipartFile(qrCodeFile); + } +} + diff --git a/sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java b/sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java new file mode 100644 index 0000000..d3afc39 --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/file/ZipUtils.java @@ -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; + } +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/http/RequestUtils.java b/sf-common/src/main/java/com/sf/common/utils/http/RequestUtils.java new file mode 100644 index 0000000..a5eb338 --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/http/RequestUtils.java @@ -0,0 +1,25 @@ +package com.sf.common.utils.http; + +import com.sf.common.utils.StringUtils; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class RequestUtils { + + public static String getHeader(String code){ + HttpServletRequest request = null; + try { + RequestAttributes requestAttributes1 = RequestContextHolder.getRequestAttributes(); + request = ((ServletRequestAttributes) requestAttributes1).getRequest(); + } catch (Exception e) { + throw new SecurityException("获取header中获取appCode信息"); + } + if(StringUtils.isBlank(request.getHeader(code))){ + throw new SecurityException("参数异常appCode为空"); + } + return request.getHeader(code); + } +} diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/AES256Util.java b/sf-common/src/main/java/com/sf/common/utils/openssl/AES256Util.java new file mode 100644 index 0000000..0058a2c --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/AES256Util.java @@ -0,0 +1,109 @@ +package com.sf.common.utils.openssl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES256Util { + private static final String ALGORITHM = "AES/ECB/PKCS5Padding"; + + public static Cipher initAESCipher(String key, int cipherMode) throws Exception { + Cipher cipher = Cipher.getInstance(ALGORITHM); + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES"); + cipher.init(cipherMode, secretKeySpec); + + return cipher; + } + + /** + * 加密文件 + * + * @return + */ + public static InputStream encryptFile(String key, InputStream inputStream) throws Exception { + Cipher cipher = initAESCipher(key, Cipher.ENCRYPT_MODE); + CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); + + return cipherInputStream; + } + + /** + * 解密文件 + * + * @param encryptedFile + * @param sourceFile + */ + public static String decryptFile(String key, File encryptedFile, File sourceFile) { + InputStream inputStream = null; + OutputStream outputStream = null; + CipherOutputStream cipherOutputStream = null; + try { + inputStream = new FileInputStream(encryptedFile); + outputStream = new FileOutputStream(sourceFile); + Cipher cipher = initAESCipher(key, Cipher.DECRYPT_MODE); + cipherOutputStream = new CipherOutputStream(outputStream, cipher); + byte[] buffer = new byte[1024]; + int n = -1; + int count = 0; + while ((n = inputStream.read(buffer)) != -1) { + count += n; + cipherOutputStream.write(buffer, 0, n); + cipherOutputStream.flush(); + } + + } catch (Exception e) { + return e.getLocalizedMessage(); + } + if (cipherOutputStream != null) { + try { + cipherOutputStream.close(); + } catch (IOException e) { + return e.getLocalizedMessage(); + } + } + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + return e.getLocalizedMessage(); + } + } + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + return e.getLocalizedMessage(); + } + } + + return ""; + } + +// public static void main(String[] args) throws Exception { +// File sourceFile = new File("/Users/a1234/Downloads/dist.zip"); +// InputStream encryptInputStream = encryptFile("efghijklmnopqrstuvwxyz0123456789", new FileInputStream(sourceFile)); +// OutputStream outputStream = new FileOutputStream(new File("/Users/a1234/Downloads/x.zip")); +// byte[] buffer = new byte[1024]; +// int n = -1; +// int count = 0; +// while ((n = encryptInputStream.read(buffer)) != -1) { +// count += n; +// outputStream.write(buffer, 0, n); +// } +// outputStream.flush(); +// System.out.println(count); +// +// decryptFile("efghijklmnopqrstuvwxyz0123456789", new File("/Users/a1234/Downloads/x.zip"), new File("/Users/a1234/Downloads/y.zip")); +// } +} + diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java b/sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java new file mode 100644 index 0000000..ed28f3b --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/Base64Utils.java @@ -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"); + } +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java b/sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java new file mode 100644 index 0000000..21eb69d --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/ByteUtil.java @@ -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(); + } + } + } + } +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java b/sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java new file mode 100644 index 0000000..ed5026e --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/openssl/RsaSignUtil.java @@ -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()); +// } +// } + +} \ No newline at end of file diff --git a/sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java b/sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java new file mode 100644 index 0000000..e0514ed --- /dev/null +++ b/sf-common/src/main/java/com/sf/common/utils/sign/Sha265Utils.java @@ -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(); + } + +} diff --git a/sf-common/src/main/java/com/sf/common/utils/uuid/IdUtils.java b/sf-common/src/main/java/com/sf/common/utils/uuid/IdUtils.java index 9379cf7..f5beff8 100644 --- a/sf-common/src/main/java/com/sf/common/utils/uuid/IdUtils.java +++ b/sf-common/src/main/java/com/sf/common/utils/uuid/IdUtils.java @@ -2,48 +2,43 @@ 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); } diff --git a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java index 55b91c5..5d332a4 100644 --- a/sf-file/src/main/java/com/sf/file/controller/SysOssController.java +++ b/sf-file/src/main/java/com/sf/file/controller/SysOssController.java @@ -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 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 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); } diff --git a/sf-file/src/main/java/com/sf/file/domain/SysOss.java b/sf-file/src/main/java/com/sf/file/domain/SysOss.java index c659941..a850fc5 100644 --- a/sf-file/src/main/java/com/sf/file/domain/SysOss.java +++ b/sf-file/src/main/java/com/sf/file/domain/SysOss.java @@ -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(); } } diff --git a/sf-file/src/main/java/com/sf/file/service/ISysOssService.java b/sf-file/src/main/java/com/sf/file/service/ISysOssService.java index 34083d1..6dfcd03 100644 --- a/sf-file/src/main/java/com/sf/file/service/ISysOssService.java +++ b/sf-file/src/main/java/com/sf/file/service/ISysOssService.java @@ -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; } diff --git a/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java b/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java index 10a9e4f..a937a9b 100644 --- a/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java +++ b/sf-file/src/main/java/com/sf/file/service/impl/SysOssServiceImpl.java @@ -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)); diff --git a/sf-framework/pom.xml b/sf-framework/pom.xml index df42e74..480011e 100644 --- a/sf-framework/pom.xml +++ b/sf-framework/pom.xml @@ -72,6 +72,18 @@ com.smarterFramework sf-common
+ + + io.springfox + springfox-boot-starter + + + + + io.swagger + swagger-models + 1.6.2 +
\ No newline at end of file diff --git a/sf-framework/src/main/java/com/sf/framework/config/SecurityConfig.java b/sf-framework/src/main/java/com/sf/framework/config/SecurityConfig.java index 3585a87..801c068 100644 --- a/sf-framework/src/main/java/com/sf/framework/config/SecurityConfig.java +++ b/sf-framework/src/main/java/com/sf/framework/config/SecurityConfig.java @@ -112,7 +112,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter // 过滤请求 .authorizeRequests() // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - .antMatchers("/login", "/register", "/captchaImage").permitAll() + .antMatchers("/login", "/register", "/captchaImage","/oauth/**").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() diff --git a/sf-framework/src/main/java/com/sf/framework/web/service/SysLoginService.java b/sf-framework/src/main/java/com/sf/framework/web/service/SysLoginService.java index 9054eb0..6ad43d9 100644 --- a/sf-framework/src/main/java/com/sf/framework/web/service/SysLoginService.java +++ b/sf-framework/src/main/java/com/sf/framework/web/service/SysLoginService.java @@ -1,6 +1,7 @@ package com.sf.framework.web.service; import javax.annotation.Resource; +import javax.security.auth.message.AuthException; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; @@ -32,6 +33,7 @@ import com.sf.framework.manager.factory.AsyncFactory; import com.sf.framework.security.context.AuthenticationContextHolder; import com.sf.system.service.ISysConfigService; import com.sf.system.service.ISysUserService; +import org.springframework.util.Assert; /** * 登录校验方法 @@ -56,13 +58,14 @@ public class SysLoginService @Autowired private ISysConfigService configService; + @Autowired + private SysPermissionService permissionService; + /** * 登录验证 - * + * * @param username 用户名 * @param password 密码 - * @param code 验证码 - * @param uuid 唯一标识 * @return 结果 */ public String login(String username, String password, String code, String uuid, HttpSession session) @@ -105,6 +108,25 @@ public class SysLoginService return tokenService.createToken(loginUser); } + /** + * 无密码登录 + * @param userName + * @return + * @author zoukun + */ + public String noPwdLogin(String userName, HttpSession session){ + SysUser user = userService.selectUserByUserName(userName); + Assert.notNull(user,MessageUtils.message("user.not.exists")); + LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + // 记录登陆信息 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGIN_SUCCESS, + MessageUtils.message("user.login.success"))); + recordLoginInfo(loginUser.getUserId()); + ApijsonUtils.buildFormSession(session, String.valueOf(loginUser.getUserId())); + return tokenService.createToken(loginUser); + } + + /** * 校验验证码 * diff --git a/sf-framework/src/main/java/com/sf/framework/web/service/SysPasswordService.java b/sf-framework/src/main/java/com/sf/framework/web/service/SysPasswordService.java index 2c48b60..ab9cf6e 100644 --- a/sf-framework/src/main/java/com/sf/framework/web/service/SysPasswordService.java +++ b/sf-framework/src/main/java/com/sf/framework/web/service/SysPasswordService.java @@ -1,6 +1,8 @@ package com.sf.framework.web.service; import java.util.concurrent.TimeUnit; + +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; @@ -23,6 +25,7 @@ import com.sf.framework.security.context.AuthenticationContextHolder; * * @author ztzh */ +@Slf4j @Component public class SysPasswordService { diff --git a/sf-oauth/pom.xml b/sf-oauth/pom.xml new file mode 100644 index 0000000..aa73cb3 --- /dev/null +++ b/sf-oauth/pom.xml @@ -0,0 +1,32 @@ + + + + smarterFramework + com.smarterFramework + 1.0.0 + + 4.0.0 + + sf-oauth + + + oauth三方登录认证模块 + + + + + + + com.smarterFramework + sf-common + + + com.smarterFramework + sf-framework + + + + + \ No newline at end of file diff --git a/sf-oauth/src/main/java/com/sf/oauth/config/AuthConfig.java b/sf-oauth/src/main/java/com/sf/oauth/config/AuthConfig.java new file mode 100644 index 0000000..3a30f11 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/config/AuthConfig.java @@ -0,0 +1,41 @@ +package com.sf.oauth.config; + +import lombok.*; + +import java.util.List; + +/** + * 配置类 + * + * @author zoukun + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthConfig { + + /** + * 客户端id:对应各平台的appKey + */ + private String clientId; + + /** + * 客户端Secret:对应各平台的appSecret + */ + private String clientSecret; + + /** + * 登录成功后的回调地址 + */ + private String redirectUri; + + /** + * 支持自定义授权平台的 scope 内容 + */ + private List scopes; + + private boolean ignoreCheckState; + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/config/AuthSource.java b/sf-oauth/src/main/java/com/sf/oauth/config/AuthSource.java new file mode 100644 index 0000000..15e6412 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/config/AuthSource.java @@ -0,0 +1,76 @@ +package com.sf.oauth.config; + +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.request.AuthDefaultRequest; + +/** + * OAuth平台的API地址的统一接口,提供以下方法: + * 1) {@link AuthSource#authorize()}: 获取授权url. 必须实现 + * 2) {@link AuthSource#accessToken()}: 获取accessToken的url. 必须实现 + * 3) {@link AuthSource#userInfo()}: 获取用户信息的url. 必须实现 + * 4) {@link AuthSource#revoke()}: 获取取消授权的url. 非必须实现接口(部分平台不支持) + * 5) {@link AuthSource#refresh()}: 获取刷新授权的url. 非必须实现接口(部分平台不支持) + *

+ * + * @author zoukun + */ +public interface AuthSource { + + /** + * 授权的api + * + * @return url + */ + String authorize(); + + /** + * 获取accessToken的api + * + * @return url + */ + String accessToken(); + + /** + * 获取用户信息的api + * + * @return url + */ + String userInfo(); + + /** + * 取消授权的api + * + * @return url + */ + default String revoke() { + throw new AuthException("UNSUPPORTED"); + } + + /** + * 刷新授权的api + * + * @return url + */ + default String refresh() { + throw new AuthException("UNSUPPORTED"); + } + + /** + * 获取Source的字符串名字 + * + * @return name + */ + default String getName() { + if (this instanceof Enum) { + return String.valueOf(this); + } + return this.getClass().getSimpleName(); + } + + /** + * 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest} + * + * @return class + */ + Class getTargetClass(); +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/controller/RestAuthController.java b/sf-oauth/src/main/java/com/sf/oauth/controller/RestAuthController.java new file mode 100644 index 0000000..036574c --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/controller/RestAuthController.java @@ -0,0 +1,143 @@ +package com.sf.oauth.controller; + +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSONObject; +import com.sf.common.constant.Constants; +import com.sf.common.constant.HttpStatus; +import com.sf.common.core.domain.AjaxResult; +import com.sf.oauth.config.AuthConfig; +import com.sf.oauth.domain.AuthCallback; +import com.sf.oauth.domain.AuthUser; +import com.sf.oauth.enums.scope.AuthHuaweiScope; +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.request.AuthHuaweiRequest; +import com.sf.oauth.request.AuthRequest; +import com.sf.oauth.service.IAuthService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.Arrays; + +import static org.apache.commons.lang3.StringUtils.EMPTY; + +/** + * @author zoukun + */ +@Slf4j +@RestController +@RequestMapping("/oauth") +public class RestAuthController { + + @Autowired + private IAuthService authService; + + @GetMapping("/authorize/{source}") + public void authorize(@PathVariable("source") String source, HttpServletResponse response) throws IOException { + log.info("进入render:" + source); + AuthRequest authRequest = getAuthRequest(source, EMPTY, EMPTY); + String authorizeUrl = authRequest.authorize(UUID.fastUUID().toString()); + log.info(authorizeUrl); + response.sendRedirect(authorizeUrl); + } + + /** + * oauth平台中配置的授权回调地址 + * 如在创建华为授权应用时的回调地址为:http://127.0.0.1:8080/oauth/callback/huawei + */ + @RequestMapping("/callback/{source}") + public AjaxResult callback(@PathVariable("source") String source, @RequestBody AuthCallback callback, HttpSession session) { + log.info("进入callback:" + source + " callback params:" + JSONObject.toJSONString(callback)); + AuthRequest authRequest = getAuthRequest(source, callback.getClientId(), callback.getClientSecret()); + AuthUser authUser = authRequest.callback(callback); + log.info(JSONObject.toJSONString(authUser)); + AjaxResult ajax = AjaxResult.success(); + String token = authService.authLogin(authUser,session); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /* + @RequestMapping("/revoke/{source}/{userId}") + @ResponseBody + public AjaxResult revokeAuth(@PathVariable("source") String source, @PathVariable("userId") String userId) throws IOException { + AuthRequest authRequest = getAuthRequest(source.toLowerCase()); + + AuthUser user = userService.getByUuid(uuid); + if (null == user) { + return Response.error("用户不存在"); + } + AjaxResult response = null; + try { + response = authRequest.revoke(user.getToken()); + if (response.ok()) { + userService.remove(user.getUuid()); + return Response.success("用户 [" + user.getUsername() + "] 的 授权状态 已收回!"); + } + return Response.error("用户 [" + user.getUsername() + "] 的 授权状态 收回失败!" + response.getMsg()); + } catch (AuthException e) { + return Response.error(e.getErrorMsg()); + } + } + + @RequestMapping("/refresh/{source}/{userId}") + @ResponseBody + public Object refreshAuth(@PathVariable("source") String source, @PathVariable("userId") String userId) { + AuthRequest authRequest = getAuthRequest(source.toLowerCase()); + + AuthUser user = userService.getByUuid(userId); + if (null == user) { + return Response.error("用户不存在"); + } + AjaxResult response = null; + try { + response = authRequest.refresh(user.getToken()); + if (response.ok()) { + user.setToken(response.getData()); + userService.save(user); + return Response.success("用户 [" + user.getUsername() + "] 的 access token 已刷新!新的 callback: " + response.getData().getAccessToken()); + } + return Response.error("用户 [" + user.getUsername() + "] 的 access token 刷新失败!" + response.getMsg()); + } catch (AuthException e) { + return Response.error(e.getErrorMsg()); + } + }*/ + + /** + * 根据具体的授权来源,获取授权请求工具类 + * + * @param source + * @return + */ + private AuthRequest getAuthRequest(String source, String clientId, String clientSecret) { + AuthRequest authRequest = null; + switch (source.toLowerCase()) { + case "huawei": + authRequest = new AuthHuaweiRequest(AuthConfig.builder() + .clientId(StrUtil.isBlank(clientId) ? "110693217" : clientId) + .clientSecret(StrUtil.isBlank(clientSecret) ? "1410c01bc71c7ba587175ae79e500137c70945acc1416a38127cf98a09a6f8ba" : clientSecret) + .redirectUri("") + .ignoreCheckState(true) + .scopes(Arrays.asList( + AuthHuaweiScope.BASE_PROFILE.getScope(), + AuthHuaweiScope.MOBILE_NUMBER.getScope(), + AuthHuaweiScope.ACCOUNTLIST.getScope(), + AuthHuaweiScope.SCOPE_DRIVE_FILE.getScope(), + AuthHuaweiScope.SCOPE_DRIVE_APPDATA.getScope() + )) + .build()); + break; + default: + break; + } + if (null == authRequest) { + throw new AuthException(HttpStatus.BAD_REQUEST, "未获取到有效的Auth配置"); + } + return authRequest; + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/domain/AuthCallback.java b/sf-oauth/src/main/java/com/sf/oauth/domain/AuthCallback.java new file mode 100644 index 0000000..403dfb8 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/domain/AuthCallback.java @@ -0,0 +1,42 @@ +package com.sf.oauth.domain; + +import lombok.*; + +import java.io.Serializable; + +/** + * 授权回调时的参数类 + * + * @author zk + */ +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthCallback implements Serializable { + + /** + * 访问AuthorizeUrl后回调时带的参数code + */ + private String code; + + /** + * 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击 + */ + private String state; + + /** + * 华为授权登录 + * + * 客户端id:对应各平台的appKey + */ + private String clientId; + + /** + * 客户端Secret:对应各平台的appSecret + */ + private String clientSecret; + + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/domain/AuthToken.java b/sf-oauth/src/main/java/com/sf/oauth/domain/AuthToken.java new file mode 100644 index 0000000..902bc1c --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/domain/AuthToken.java @@ -0,0 +1,42 @@ +package com.sf.oauth.domain; + +import lombok.*; + +import java.io.Serializable; + +/** + * 授权所需的token + * + * @author zoukun + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthToken implements Serializable { + private String accessToken; + private int expireIn; + private String refreshToken; + private int refreshTokenExpireIn; + private String uid; + private String openId; + private String accessCode; + private String unionId; + + /** + * 华为返回 生成的Access Token中包含的scope。 + */ + private String scope; + + /** + * 华为返回 固定返回Bearer,标识返回Access Token的类型 + */ + private String tokenType; + + /** + * 华为返回 返回JWT格式数据,包含用户基本帐号、用户邮箱等信息。 + * 参照https://developer.huawei.com/consumer/cn/doc/HMSCore-References/account-verify-id-token_hms_reference-0000001050050577#section3142132691914 + */ + private String idToken; +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/domain/AuthUser.java b/sf-oauth/src/main/java/com/sf/oauth/domain/AuthUser.java new file mode 100644 index 0000000..1201e16 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/domain/AuthUser.java @@ -0,0 +1,79 @@ +package com.sf.oauth.domain; + + +import com.alibaba.fastjson2.JSONObject; +import com.sf.oauth.enums.AuthUserGender; +import lombok.*; + +import java.io.Serializable; + +/** + * 授权成功后的用户信息,根据授权平台的不同,获取的数据完整性也不同 + * + * @author zoukun + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthUser implements Serializable { + /** + * 用户第三方系统的唯一id。 + */ + private String uuid; + /** + * 用户名 + */ + private String username; + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 用户网址 + */ + private String blog; + /** + * 所在公司 + */ + private String company; + /** + * 位置 + */ + private String location; + /** + * 用户邮箱 + */ + private String email; + /** + * 用户手机号 + */ + private String mobileNumber; + /** + * 用户备注(各平台中的用户个人介绍) + */ + private String remark; + /** + * 性别 + */ + private AuthUserGender gender; + /** + * 用户来源 + */ + private String source; + /** + * 用户授权的token信息 + */ + private AuthToken token; + /** + * 第三方平台返回的原始用户信息 + */ + private JSONObject rawUserInfo; + + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/enums/AuthDefaultSource.java b/sf-oauth/src/main/java/com/sf/oauth/enums/AuthDefaultSource.java new file mode 100644 index 0000000..789a4a0 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/enums/AuthDefaultSource.java @@ -0,0 +1,47 @@ +package com.sf.oauth.enums; + +import com.sf.oauth.config.AuthSource; +import com.sf.oauth.request.AuthDefaultRequest; +import com.sf.oauth.request.AuthHuaweiRequest; + +/** + * 内置的各api需要的url, 用枚举类分平台类型管理 + * + * @author zoukun + */ +public enum AuthDefaultSource implements AuthSource { + + + /** + * 华为oauth2/v3 + */ + HUAWEI { + @Override + public String authorize() { + return "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize"; + } + + @Override + public String accessToken() { + return "https://oauth-login.cloud.huawei.com/oauth2/v3/token"; + } + + @Override + public String userInfo() { + return "https://account.cloud.huawei.com/rest.php"; + } + + @Override + public String refresh() { + return "https://oauth-login.cloud.huawei.com/oauth2/v3/token"; + } + + @Override + public Class getTargetClass() { + return AuthHuaweiRequest.class; + } + }, + + + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/enums/AuthPlatformInfo.java b/sf-oauth/src/main/java/com/sf/oauth/enums/AuthPlatformInfo.java new file mode 100644 index 0000000..ae1aa39 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/enums/AuthPlatformInfo.java @@ -0,0 +1,51 @@ +package com.sf.oauth.enums; + +import java.util.*; + +/** + * @author zoukun + */ +public enum AuthPlatformInfo { + + /** + * 平台 + */ + HUAWEI("华为", "huawei", "https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/open-platform-oauth-0000001053629189"), + ; + + /** + * 平台名 + */ + private final String name; + /** + * 平台编码 + */ + private final String code; + /** + * 官网api文档 + */ + private final String apiDoc; + + AuthPlatformInfo(String name, String code, String apiDoc) { + this.name = name; + this.code = code; + this.apiDoc = apiDoc; + } + + public static List getPlatformInfos() { + return Arrays.asList(AuthPlatformInfo.values()); + } + + public String getName() { + return name; + } + + public String getCode() { + return code; + } + + public String getApiDoc() { + return apiDoc; + } + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/enums/AuthUserGender.java b/sf-oauth/src/main/java/com/sf/oauth/enums/AuthUserGender.java new file mode 100644 index 0000000..7a76bf3 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/enums/AuthUserGender.java @@ -0,0 +1,45 @@ +package com.sf.oauth.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户性别 + * + * @author zoukun + */ +@Getter +@AllArgsConstructor +public enum AuthUserGender { + /** + * MALE/FAMALE为正常值,通过{@link AuthUserGender#getRealGender(String)}方法获取真实的性别 + * UNKNOWN为容错值,部分平台不会返回用户性别,为了方便统一,使用UNKNOWN标记所有未知或不可测的用户性别信息 + */ + MALE("0", "男"), + FEMALE("1", "女"), + UNKNOWN("2", "未知"); + + private String code; + private String desc; + + /** + * 获取用户的实际性别,常规网站 + * + * @param originalGender 用户第三方标注的原始性别 + * @return 用户性别 + */ + public static AuthUserGender getRealGender(String originalGender) { + if (null == originalGender || UNKNOWN.getCode().equals(originalGender)) { + return UNKNOWN; + } + String[] males = {"m", "男", "0", "male"}; + if (Arrays.asList(males).contains(originalGender.toLowerCase())) { + return MALE; + } + return FEMALE; + } + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/enums/scope/AuthHuaweiScope.java b/sf-oauth/src/main/java/com/sf/oauth/enums/scope/AuthHuaweiScope.java new file mode 100644 index 0000000..4d61bb0 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/enums/scope/AuthHuaweiScope.java @@ -0,0 +1,44 @@ +package com.sf.oauth.enums.scope; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 华为平台 OAuth 授权范围 + * + * @author zoukun + */ +@Getter +@AllArgsConstructor +public enum AuthHuaweiScope implements AuthScope { + + /** + * {@code scope} 含义,以{@code description} 为准 + */ + BASE_PROFILE("https://www.huawei.com/auth/account/base.profile", "获取用户的基本信息", true), + MOBILE_NUMBER("https://www.huawei.com/auth/account/mobile.number", "获取用户的手机号", false), + ACCOUNTLIST("https://www.huawei.com/auth/account/accountlist", "获取用户的账单列表", false), + + /** + * 以下两个 scope 不需要经过华为评估和验证 + */ + SCOPE_DRIVE_FILE("https://www.huawei.com/auth/drive.file", "只允许访问由应用程序创建或打开的文件", false), + SCOPE_DRIVE_APPDATA("https://www.huawei.com/auth/drive.appdata", "只允许访问由应用程序创建或打开的文件", false), + /** + * 以下四个 scope 使用前需要向drivekit@huawei.com提交申请 + *

+ * 参考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides-V5/server-dev-0000001050039664-V5#ZH-CN_TOPIC_0000001050039664__section1618418855716 + */ + SCOPE_DRIVE("https://www.huawei.com/auth/drive", "只允许访问由应用程序创建或打开的文件", false), + SCOPE_DRIVE_READONLY("https://www.huawei.com/auth/drive.readonly", "只允许访问由应用程序创建或打开的文件", false), + SCOPE_DRIVE_METADATA("https://www.huawei.com/auth/drive.metadata", "只允许访问由应用程序创建或打开的文件", false), + SCOPE_DRIVE_METADATA_READONLY("https://www.huawei.com/auth/drive.metadata.readonly", "只允许访问由应用程序创建或打开的文件", false), + + + ; + + private final String scope; + private final String description; + private final boolean isDefault; + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/enums/scope/AuthScope.java b/sf-oauth/src/main/java/com/sf/oauth/enums/scope/AuthScope.java new file mode 100644 index 0000000..52ef1d4 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/enums/scope/AuthScope.java @@ -0,0 +1,23 @@ +package com.sf.oauth.enums.scope; + +/** + * 各个平台 scope 类的统一接口 + * + * @author zoukun + */ +public interface AuthScope { + + /** + * 获取字符串 {@code scope},对应为各平台实际使用的 {@code scope} + * + * @return String + */ + String getScope(); + + /** + * 判断当前 {@code scope} 是否为各平台默认启用的 + * + * @return boolean + */ + boolean isDefault(); +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/exception/AuthException.java b/sf-oauth/src/main/java/com/sf/oauth/exception/AuthException.java new file mode 100644 index 0000000..b0f6959 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/exception/AuthException.java @@ -0,0 +1,46 @@ +package com.sf.oauth.exception; + + +import com.sf.oauth.config.AuthSource; + +/** + * 授权异常 + * + * @author zoukun + */ +public class AuthException extends RuntimeException { + + private int errorCode; + private String errorMsg; + + public AuthException(String errorMsg) { + super(errorMsg); + this.errorMsg = errorMsg; + } + + public AuthException(int errorCode, String errorMsg) { + super(errorMsg); + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + public AuthException(int errorCode, String errorMsg, AuthSource source) { + this(errorCode, String.format("%s [%s]", errorMsg, source.getName())); + } + + public AuthException(String message, Throwable cause) { + super(message, cause); + } + + public AuthException(Throwable cause) { + super(cause); + } + + public int getErrorCode() { + return errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/request/AuthDefaultRequest.java b/sf-oauth/src/main/java/com/sf/oauth/request/AuthDefaultRequest.java new file mode 100644 index 0000000..6a9f991 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/request/AuthDefaultRequest.java @@ -0,0 +1,177 @@ +package com.sf.oauth.request; + +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.http.HttpUtil; +import com.sf.common.constant.HttpStatus; +import com.sf.oauth.config.AuthConfig; +import com.sf.oauth.config.AuthSource; +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.domain.AuthCallback; +import com.sf.oauth.domain.AuthToken; +import com.sf.oauth.domain.AuthUser; +import com.sf.oauth.utils.AuthChecker; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 默认的request处理类 + * + * @author zoukun + */ +@Slf4j +public abstract class AuthDefaultRequest implements AuthRequest { + protected AuthConfig config; + protected AuthSource source; + + public AuthDefaultRequest(AuthConfig config, AuthSource source) { + this.config = config; + this.source = source; + if (!AuthChecker.isSupportedAuth(config, source)) { + throw new AuthException(HttpStatus.ERROR,"Parameter incomplete", source); + } + // 校验配置合法性 + // AuthChecker.checkConfig(config, source); + } + + /** + * 获取access token + * + * @param authCallback 授权成功后的回调参数 + * @return token + * @see AuthDefaultRequest#authorize() + * @see AuthDefaultRequest#authorize(String) + */ + protected abstract AuthToken getAccessToken(AuthCallback authCallback); + + /** + * 使用token换取用户信息 + * + * @param authToken token信息 + * @return 用户信息 + * @see AuthDefaultRequest#getAccessToken(AuthCallback) + */ + protected abstract AuthUser getUserInfo(AuthToken authToken); + + /** + * 统一的登录入口。当通过{@link AuthDefaultRequest#authorize(String)}授权成功后,会跳转到调用方的相关回调方法中 + * 方法的入参可以使用{@code AuthCallback},{@code AuthCallback}类中封装好了OAuth2授权回调所需要的参数 + * + * @param authCallback 用于接收回调参数的实体 + * @return AuthResponse + */ + @Override + public AuthUser callback(AuthCallback authCallback) { + try { + checkCode(authCallback); + if (!config.isIgnoreCheckState()) { + AuthChecker.checkState(authCallback.getState(), source); + } + + AuthToken authToken = this.getAccessToken(authCallback); + return this.getUserInfo(authToken); + } catch (Exception e) { + log.error("Failed to callback with oauth authorization", e); + throw new AuthException(HttpStatus.UNAUTHORIZED,"Failed to callback with oauth authorization"); + } + } + + protected void checkCode(AuthCallback authCallback) { + AuthChecker.checkCode(source, authCallback); + } + + + /** + * 返回授权url,可自行跳转页面 + *

+ * 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。 + * 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验 + * + * @return 返回授权地址 + * @see AuthDefaultRequest#authorize(String) + */ + @Override + public String authorize() { + return this.authorize(null); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + @Override + public String authorize(String state) { + Map form = new HashMap<>(8); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("redirect_uri", config.getRedirectUri()); + form.put("state", getRealState(state)); + form.put("response_type","code"); + return HttpUtil.get(source.authorize(), form); + } + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + protected String accessTokenUrl(String code) { + Map form = new HashMap<>(8); + form.put("code", code); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("grant_type", "authorization_code"); + form.put("redirect_uri", config.getRedirectUri()); + return HttpUtil.get(source.accessToken(),form); + } + + + + + /** + * 获取state,如果为空, 则默认取当前日期的时间戳 + * + * @param state 原始的state + * @return 返回不为null的state + */ + protected String getRealState(String state) { + if (StrUtil.isEmpty(state)) { + state = UUID.fastUUID().toString(); + } + // todo 需要缓存state + return state; + } + + + /** + * 获取以 {@code separator}分割过后的 scope 信息 + * + * @param separator 多个 {@code scope} 间的分隔符 + * @param encode 是否 encode 编码 + * @param defaultScopes 默认的 scope, 当客户端没有配置 {@code scopes} 时启用 + * @return String + */ + protected String getScopes(String separator, boolean encode, List defaultScopes) { + List scopes = config.getScopes(); + if (null == scopes || scopes.isEmpty()) { + if (null == defaultScopes || defaultScopes.isEmpty()) { + return ""; + } + scopes = defaultScopes; + } + if (null == separator) { + // 默认为空格 + separator = " "; + } + String scopeStr = String.join(separator, scopes); + return encode ? URLUtil.encode(scopeStr) : scopeStr; + } + +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/request/AuthHuaweiRequest.java b/sf-oauth/src/main/java/com/sf/oauth/request/AuthHuaweiRequest.java new file mode 100644 index 0000000..065caf7 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/request/AuthHuaweiRequest.java @@ -0,0 +1,168 @@ +package com.sf.oauth.request; + + +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson2.JSONObject; +import com.sf.common.constant.HttpStatus; +import com.sf.common.core.domain.AjaxResult; +import com.sf.oauth.config.AuthConfig; +import com.sf.oauth.enums.AuthDefaultSource; +import com.sf.oauth.enums.AuthUserGender; +import com.sf.oauth.enums.scope.AuthHuaweiScope; +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.domain.AuthCallback; +import com.sf.oauth.domain.AuthToken; +import com.sf.oauth.domain.AuthUser; +import com.sf.oauth.utils.AuthScopeUtils; +import io.jsonwebtoken.Jwts; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; + + +/** + * 华为授权登录 + * + * @author zkun + */ +@Slf4j +public class AuthHuaweiRequest extends AuthDefaultRequest { + + public AuthHuaweiRequest(AuthConfig config) { + super(config, AuthDefaultSource.HUAWEI); + } + + + /** + * 获取access token + * + * @param authCallback 授权成功后的回调参数 + * @return token + * @see AuthDefaultRequest#authorize() + * @see AuthDefaultRequest#authorize(String) + */ + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + Map form = new HashMap<>(8); + form.put("grant_type", "authorization_code"); + form.put("code", authCallback.getCode()); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("redirect_uri", config.getRedirectUri()); + String response = HttpUtil.post(source.accessToken(), form); + return getAuthToken(response); + } + + /** + * 使用token换取用户信息 + * + * @param authToken token信息 + * @return 用户信息 + * @see AuthDefaultRequest#getAccessToken(AuthCallback) + */ + @Override + protected AuthUser getUserInfo(AuthToken authToken) { + Map form = new HashMap<>(8); + form.put("nsp_ts", System.currentTimeMillis() + ""); + form.put("access_token", authToken.getAccessToken()); + form.put("nsp_svc", "GOpen.User.getInfo"); + form.put("getNickName", "1"); + String response = HttpUtil.post(source.userInfo(), form); + log.info(response); + + JSONObject object = JSONObject.parseObject(response); + this.checkResponse(object); + AuthUserGender gender = getRealGender(object); + return AuthUser.builder() + .rawUserInfo(object) + .uuid(object.getString("unionID")) + .username(object.getString("displayName")) + .nickname(object.getString("displayName")) + .gender(gender) + .avatar(object.getString("headPictureURL")) + .mobileNumber(object.getString("mobileNumber")) + .email(object.getString("email")) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 刷新access token (续期) + * + * @param authToken 登录成功后返回的Token信息 + * @return AuthResponse + */ + @Override + public AjaxResult refresh(AuthToken authToken) { + Map form = new HashMap<>(8); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("refresh_token", authToken.getRefreshToken()); + form.put("grant_type", "refresh_token"); + String response = HttpUtil.post(source.refresh(), form); + return AjaxResult.success(getAuthToken(response)); + } + + private AuthToken getAuthToken(String response) { + JSONObject object = JSONObject.parseObject(response); + + this.checkResponse(object); + + return AuthToken.builder() + .accessToken(object.getString("access_token")) + .expireIn(object.getIntValue("expires_in")) + .refreshToken(object.getString("refresh_token")) + .scope(object.getString("scope")) + .tokenType(object.getString("token_type")) + .idToken(object.getString("id_token")) + .build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + @Override + public String authorize(String state) { + Map form = new HashMap<>(8); + form.put("client_id", config.getClientId()); + form.put("client_secret", config.getClientSecret()); + form.put("redirect_uri", config.getRedirectUri()); + form.put("state", getRealState(state)); + form.put("response_type", "code"); + form.put("access_type", "offline"); + form.put("scope", this.getScopes(" ", true, AuthScopeUtils.getDefaultScopes(AuthHuaweiScope.values()))); + return HttpUtil.get(source.authorize(), form); + } + + + /** + * 获取用户的实际性别。华为系统中,用户的性别:1表示女,0表示男 + * + * @param object obj + * @return AuthUserGender + */ + private AuthUserGender getRealGender(JSONObject object) { + int genderCodeInt = object.getIntValue("gender"); + String genderCode = genderCodeInt == 0 ? "0" : (genderCodeInt == 1) ? "1" : genderCodeInt + ""; + return AuthUserGender.getRealGender(genderCode); + } + + /** + * 校验响应结果 + * + * @param object 接口返回的结果 + */ + private void checkResponse(JSONObject object) { + if (object.containsKey("NSP_STATUS")) { + throw new AuthException(object.getString("error")); + } + if (object.containsKey("error")) { + throw new AuthException(object.getString("sub_error") + ":" + object.getString("error_description")); + } + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/request/AuthRequest.java b/sf-oauth/src/main/java/com/sf/oauth/request/AuthRequest.java new file mode 100644 index 0000000..5691774 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/request/AuthRequest.java @@ -0,0 +1,76 @@ +package com.sf.oauth.request; + + +import com.sf.common.constant.HttpStatus; +import com.sf.common.core.domain.AjaxResult; +import com.sf.oauth.domain.AuthUser; +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.domain.AuthCallback; +import com.sf.oauth.domain.AuthToken; + +/** + * {@code Request}公共接口,所有平台的{@code Request}都需要实现该接口 + *

+ * {@link AuthRequest#authorize()} + * {@link AuthRequest#authorize(String)} + * {@link AuthRequest#callback(AuthCallback)} + * {@link AuthRequest#revoke(AuthToken)} + * {@link AuthRequest#refresh(AuthToken)} + * + * @author zoukun + */ +public interface AuthRequest { + + /** + * 返回授权url,可自行跳转页面 + *

+ * 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。 + * 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验 + * + * @return 返回授权地址 + */ + @Deprecated + default String authorize() { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Not Implemented"); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + default String authorize(String state) { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Not Implemented"); + } + + /** + * 第三方登录 + * + * @param authCallback 用于接收回调参数的实体 + * @return 返回登录成功后的用户信息 + */ + default AuthUser callback(AuthCallback authCallback) { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Not Implemented"); + } + + /** + * 撤销授权 + * + * @param authToken 登录成功后返回的Token信息 + * @return AjaxResult + */ + default AjaxResult revoke(AuthToken authToken) { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Not Implemented"); + } + + /** + * 刷新access token (续期) + * + * @param authToken 登录成功后返回的Token信息 + * @return AjaxResult + */ + default AjaxResult refresh(AuthToken authToken) { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Not Implemented"); + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/service/IAuthService.java b/sf-oauth/src/main/java/com/sf/oauth/service/IAuthService.java new file mode 100644 index 0000000..d86faa2 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/service/IAuthService.java @@ -0,0 +1,15 @@ +package com.sf.oauth.service; + +import com.sf.oauth.domain.AuthUser; + +import javax.servlet.http.HttpSession; + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/12 10:21 + */ +public interface IAuthService { + String authLogin(AuthUser authUser, HttpSession session); +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/service/impl/IAuthServiceImpl.java b/sf-oauth/src/main/java/com/sf/oauth/service/impl/IAuthServiceImpl.java new file mode 100644 index 0000000..82d2cc0 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/service/impl/IAuthServiceImpl.java @@ -0,0 +1,62 @@ +package com.sf.oauth.service.impl; + +import com.sf.common.core.domain.AjaxResult; +import com.sf.common.core.domain.entity.SysUser; +import com.sf.common.enums.UserStatus; +import com.sf.common.utils.SecurityUtils; +import com.sf.common.utils.ip.IpUtils; +import com.sf.framework.web.service.SysLoginService; +import com.sf.oauth.domain.AuthUser; +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.service.IAuthService; +import com.sf.system.service.ISysUserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpSession; +import java.util.Date; + +/** + * 功能描述: + * + * @author a_kun + * @date 2024/4/12 10:21 + */ +@Slf4j +@Service +public class IAuthServiceImpl implements IAuthService { + + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysUserService userService; + + + @Override + public String authLogin(AuthUser authUser, HttpSession session) { + // TODO 应该要登录后绑定一个平台账号,没有平台账号要创建,保存用户的三方来源,及用户的三方唯一标识和绑定的用户 + if (authUser != null) { + SysUser sysUser = new SysUser(); + sysUser.setUserName(authUser.getUsername()); + if (userService.checkUserNameUnique(sysUser)) { + // 注册用户后登录 + sysUser.setNickName(authUser.getNickname()); + sysUser.setEmail(authUser.getEmail()); + sysUser.setPhonenumber(authUser.getMobileNumber()); + sysUser.setSex(authUser.getGender().getCode()); + sysUser.setAvatar(authUser.getAvatar()); + sysUser.setPassword(SecurityUtils.encryptPassword("ztzh@sac123")); + sysUser.setStatus(UserStatus.OK.getCode()); + sysUser.setDelFlag("0"); + sysUser.setLoginIp(IpUtils.getHostIp()); + sysUser.setLoginDate(new Date()); + userService.insertUser(sysUser); + } + // 生成令牌 + return loginService.noPwdLogin(authUser.getUsername(), session); + } + throw new AuthException("login error"); + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/utils/AuthChecker.java b/sf-oauth/src/main/java/com/sf/oauth/utils/AuthChecker.java new file mode 100644 index 0000000..9fe8961 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/utils/AuthChecker.java @@ -0,0 +1,79 @@ +package com.sf.oauth.utils; + + +import cn.hutool.core.util.StrUtil; +import com.sf.common.constant.HttpStatus; +import com.sf.oauth.config.AuthConfig; +import com.sf.oauth.enums.AuthDefaultSource; +import com.sf.oauth.config.AuthSource; +import com.sf.oauth.exception.AuthException; +import com.sf.oauth.domain.AuthCallback; + +/** + * 授权配置类的校验器 + * + * @author ZK + */ +public class AuthChecker { + + /** + * 是否支持第三方登录 + * + * @param config config + * @param source source + * @return true or false + */ + public static boolean isSupportedAuth(AuthConfig config, AuthSource source) { + + return StrUtil.isNotEmpty(config.getClientId()) + && StrUtil.isNotEmpty(config.getClientSecret()) + && null != source; + } + + /** + * 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://, + * 而对于部分平台, redirect uri 必须是https的链接 + * + * @param config config + * @param source source + */ + public static void checkConfig(AuthConfig config, AuthSource source) { + String redirectUri = config.getRedirectUri(); + if (StrUtil.isEmpty(redirectUri)) { + throw new AuthException(HttpStatus.BAD_REQUEST, "Illegal redirect uri", source); + } + if (!AuthUtils.isHttpProtocol(redirectUri) && !AuthUtils.isHttpsProtocol(redirectUri)) { + throw new AuthException(HttpStatus.BAD_REQUEST, "Illegal redirect uri", source); + } + } + + /** + * 校验回调传回的code + *

+ * {@code v1.10.0}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理 + * + * @param source 当前授权平台 + * @param callback 从第三方授权回调回来时传入的参数集合 + */ + public static void checkCode(AuthSource source, AuthCallback callback) { + if (StrUtil.isEmpty(callback.getCode())) { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Illegal code", source); + } + } + + /** + * 校验回调传回的{@code state},为空或者不存在 + *

+ * {@code state}不存在的情况只有两种: + * 1. {@code state}已使用,被正常清除 + * 2. {@code state}为前端伪造,本身就不存在 + * + * @param state {@code state}一定不为空 + * @param source {@code source}当前授权平台 + */ + public static void checkState(String state, AuthSource source) { + if (StrUtil.isEmpty(state)) { + throw new AuthException(HttpStatus.UNAUTHORIZED,"Illegal state", source); + } + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/utils/AuthScopeUtils.java b/sf-oauth/src/main/java/com/sf/oauth/utils/AuthScopeUtils.java new file mode 100644 index 0000000..9350882 --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/utils/AuthScopeUtils.java @@ -0,0 +1,44 @@ +package com.sf.oauth.utils; + +import com.sf.oauth.enums.scope.AuthScope; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Scope 工具类,提供对 scope 类的统一操作 + * + * @author zoukun + */ +public class AuthScopeUtils { + + /** + * 获取 {@link com.sf.oauth.enums.scope.AuthScope} 数组中所有的被标记为 {@code default} 的 scope + * + * @param scopes scopes + * @return List + */ + public static List getDefaultScopes(AuthScope[] scopes) { + if (null == scopes || scopes.length == 0) { + return null; + } + return Arrays.stream(scopes) + .filter((AuthScope::isDefault)) + .map(AuthScope::getScope) + .collect(Collectors.toList()); + } + + /** + * 从 {@link com.sf.oauth.enums.scope.AuthScope} 数组中获取实际的 scope 字符串 + * + * @param scopes 可变参数,支持传任意 {@link com.sf.oauth.enums.scope.AuthScope} + * @return List + */ + public static List getScopes(AuthScope... scopes) { + if (null == scopes || scopes.length == 0) { + return null; + } + return Arrays.stream(scopes).map(AuthScope::getScope).collect(Collectors.toList()); + } +} diff --git a/sf-oauth/src/main/java/com/sf/oauth/utils/AuthUtils.java b/sf-oauth/src/main/java/com/sf/oauth/utils/AuthUtils.java new file mode 100644 index 0000000..3d3e22c --- /dev/null +++ b/sf-oauth/src/main/java/com/sf/oauth/utils/AuthUtils.java @@ -0,0 +1,127 @@ + +package com.sf.oauth.utils; + +import cn.hutool.core.util.StrUtil; +import com.sf.oauth.exception.AuthException; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class AuthUtils { + private static final Charset DEFAULT_ENCODING; + private static final String HMAC_SHA1 = "HmacSHA1"; + private static final String HMAC_SHA_256 = "HmacSHA256"; + + public AuthUtils() { + } + + public static String urlEncode(String value) { + if (value == null) { + return ""; + } else { + try { + String encoded = URLEncoder.encode(value, DEFAULT_ENCODING.displayName()); + return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F"); + } catch (UnsupportedEncodingException var2) { + throw new AuthException("Failed To Encode Uri", var2); + } + } + } + + public static String urlDecode(String value) { + if (value == null) { + return ""; + } else { + try { + return URLDecoder.decode(value, DEFAULT_ENCODING.displayName()); + } catch (UnsupportedEncodingException var2) { + throw new AuthException("Failed To Decode Uri", var2); + } + } + } + + + public static String parseMapToString(Map params, boolean encode) { + if (null != params && !params.isEmpty()) { + List paramList = new ArrayList(); + params.forEach((k, v) -> { + if (null == v) { + paramList.add(k + "="); + } else { + paramList.add(k + "=" + (encode ? urlEncode(v) : v)); + } + + }); + return String.join("&", paramList); + } else { + return ""; + } + } + + public static boolean isHttpProtocol(String url) { + if (StrUtil.isEmpty(url)) { + return false; + } else { + return url.startsWith("http://") || url.startsWith("http%3A%2F%2F"); + } + } + + public static boolean isHttpsProtocol(String url) { + if (StrUtil.isEmpty(url)) { + return false; + } else { + return url.startsWith("https://") || url.startsWith("https%3A%2F%2F"); + } + } + + public static boolean isLocalHost(String url) { + return StrUtil.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost"); + } + + public static boolean isHttpsProtocolOrLocalHost(String url) { + if (StrUtil.isEmpty(url)) { + return false; + } else { + return isHttpsProtocol(url) || isLocalHost(url); + } + } + + + public static String getTimestamp() { + return String.valueOf(System.currentTimeMillis() / 1000L); + } + + + public static String md5(String str) { + MessageDigest md = null; + StringBuilder buffer = null; + + try { + md = MessageDigest.getInstance("MD5"); + md.update(str.getBytes(StandardCharsets.UTF_8)); + byte[] byteData = md.digest(); + buffer = new StringBuilder(); + byte[] var4 = byteData; + int var5 = byteData.length; + + for(int var6 = 0; var6 < var5; ++var6) { + byte byteDatum = var4[var6]; + buffer.append(Integer.toString((byteDatum & 255) + 256, 16).substring(1)); + } + } catch (Exception var8) { + } + + return null == buffer ? "" : buffer.toString(); + } + + static { + DEFAULT_ENCODING = StandardCharsets.UTF_8; + } +} diff --git a/sf-order/pom.xml b/sf-order/pom.xml new file mode 100644 index 0000000..d95e016 --- /dev/null +++ b/sf-order/pom.xml @@ -0,0 +1,33 @@ + + + + smarterFramework + com.smarterFramework + 1.0.0 + + 4.0.0 + + sf-order + + + 订单模块 + + + + + + + com.smarterFramework + sf-common + + + com.smarterFramework + sf-framework + + + + + + \ No newline at end of file diff --git a/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java b/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java new file mode 100644 index 0000000..37b7d23 --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/controller/OrderInfoController.java @@ -0,0 +1,115 @@ +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.page.TableDataInfo; +import com.sf.common.enums.BusinessType; +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.http.RequestUtils; +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 订单基础信息Controller + * + * @author ztzh + * @date 2024-04-09 + */ +@RestController +@RequestMapping("/order") +public class OrderInfoController extends BaseController { + + @Autowired + private IOrderInfoService orderInfoService; + + /** + * 查询订单基础信息列表 + */ + @GetMapping("/list") + public TableDataInfo list(OrderListReqVo vo) { + vo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + startPage(); + List list = orderInfoService.queryList(vo); + return getDataTable(list); + } + + /** + * 我的订单列表(当前用户) + */ + @GetMapping("/myOrderList") + public TableDataInfo myOrderList(OrderListReqVo vo) { + vo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + vo.setUserId(getUserId()); + // startPage(); + List list = orderInfoService.queryList(vo); + return getDataTable(list); + } + + + + /** + * 导出订单基础信息列表 + */ + @Log(title = "订单基础信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, OrderInfo orderInfo) { + List list = orderInfoService.selectOrderInfoList(orderInfo); + ExcelUtil util = new ExcelUtil(OrderInfo.class); + util.exportExcel(response, list, "订单基础信息数据"); + } + + /** + * 获取订单基础信息详细信息 + */ + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) { + return success(orderInfoService.selectOrderInfoById(id)); + } + + /** + * 新增订单基础信息 + */ + @Log(title = "创建订单基础信息", businessType = BusinessType.INSERT) + @PostMapping(value = "/createOrder") + public AjaxResult createOrder(@RequestBody OrderCreateDto orderCreateDto) { + orderCreateDto.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + return AjaxResult.success(orderInfoService.createOrder(orderCreateDto)); + } + + /** + * 修改订单基础信息 + */ + @Log(title = "订单基础信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody OrderInfo orderInfo) { + return toAjax(orderInfoService.updateOrderInfo(orderInfo)); + } + + /** + * 支付订单 + */ + @PostMapping(value = "/pay/{orderId}") + private String orderPay(@PathVariable(value = "orderId") Long orderId) { + orderInfoService.orderPay(orderId); + return null; + } + + /** + * 删除订单基础信息 + */ + @Log(title = "订单基础信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) { + return toAjax(orderInfoService.deleteOrderInfoByIds(ids)); + } +} diff --git a/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java b/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java new file mode 100644 index 0000000..ca65473 --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/domain/OrderInfo.java @@ -0,0 +1,187 @@ +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:已完成 + */ + @Excel(name = "订单状态: 0:待支付 1:已付款 2:支付超时系统结束 3:客户自主取消 4:已完成") + 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; + /** + * appCode + */ + private String appCode; + + +} diff --git a/sf-order/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java b/sf-order/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java new file mode 100644 index 0000000..3107020 --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/domain/dto/OrderCreateDto.java @@ -0,0 +1,80 @@ +package com.sf.order.domain.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import javax.validation.constraints.NotNull; + +/** + * @author : ztzh + * @since : 2023/7/27 + */ +@Schema(description = "下单") +public class OrderCreateDto { + + @Schema(description = "商品id") + @NotNull(message = "商品id不能为空") + private Long goodsId; + + @Schema(description = "平台外部订单号") + private String outOrderNo; + + @Schema(description = "数量") + private Long count; + + @Schema(description = "金额") + private Long amount; + + @Schema(description = "用户id") + @NotNull(message = "用户id不能空") + private Long userId; + @Schema(description = "应用code") + @NotNull(message = "应用code不能空") + private String appCode; + + public Long getGoodsId() { + return goodsId; + } + + public void setGoodsId(Long goodsId) { + this.goodsId = goodsId; + } + public Long getCount() { + return count; + } + + public void setCount(Long count) { + this.count = count; + } + + public Long getAmount() { + return amount; + } + + public void setAmount(Long amount) { + this.amount = amount; + } + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getOutOrderNo() { + return outOrderNo; + } + + public void setOutOrderNo(String outOrderNo) { + this.outOrderNo = outOrderNo; + } + + public String getAppCode() { + return appCode; + } + + public void setAppCode(String appCode) { + this.appCode = appCode; + } + +} diff --git a/sf-order/src/main/java/com/sf/order/domain/req/OrderListReqVo.java b/sf-order/src/main/java/com/sf/order/domain/req/OrderListReqVo.java new file mode 100644 index 0000000..b640967 --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/domain/req/OrderListReqVo.java @@ -0,0 +1,27 @@ +package com.sf.order.domain.req; + +import com.sf.common.core.domain.BaseEntity; +import lombok.Data; + +@Data +public class OrderListReqVo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + private Long userId; + + /** + * 订单状态 + */ + private Long orderStatus; + + private Integer pageSize; + + private Integer pageNo; + + private String appCode; + +} diff --git a/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java b/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java new file mode 100644 index 0000000..85533be --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/domain/res/OrderListResVo.java @@ -0,0 +1,97 @@ +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; + /** + * 应用编码 + */ + private String appCode; + + +} diff --git a/sf-order/src/main/java/com/sf/order/mapper/OrderInfoMapper.java b/sf-order/src/main/java/com/sf/order/mapper/OrderInfoMapper.java new file mode 100644 index 0000000..342981a --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/mapper/OrderInfoMapper.java @@ -0,0 +1,69 @@ +package com.sf.order.mapper; + +import com.sf.order.domain.OrderInfo; +import com.sf.order.domain.req.OrderListReqVo; +import com.sf.order.domain.res.OrderListResVo; + +import java.util.List; + + +/** + * 订单基础信息Mapper接口 + * + * @author ztzh + * @date 2024-04-09 + */ +public interface OrderInfoMapper { + /** + * 查询订单基础信息 + * + * @param id 订单基础信息主键 + * @return 订单基础信息 + */ + public OrderInfo selectOrderInfoById(Long id); + + /** + * 查询订单基础信息列表 + * + * @param orderInfo 订单基础信息 + * @return 订单基础信息集合 + */ + public List selectOrderInfoList(OrderInfo orderInfo); + + + List queryList(OrderListReqVo vo); + + /** + * 新增订单基础信息 + * + * @param orderInfo 订单基础信息 + * @return 结果 + */ + public int insertOrderInfo(OrderInfo orderInfo); + + /** + * 修改订单基础信息 + * + * @param orderInfo 订单基础信息 + * @return 结果 + */ + public int updateOrderInfo(OrderInfo orderInfo); + + /** + * 删除订单基础信息 + * + * @param id 订单基础信息主键 + * @return 结果 + */ + public int deleteOrderInfoById(Long id); + + /** + * 批量删除订单基础信息 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteOrderInfoByIds(Long[] ids); + + OrderInfo selectOrderInfoByOrderNo(String orderNo); +} diff --git a/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java b/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java new file mode 100644 index 0000000..9904bb6 --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/service/IOrderInfoService.java @@ -0,0 +1,78 @@ +package com.sf.order.service; + + +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 java.util.List; + +/** + * 订单基础信息Service接口 + * + * @author ztzh + * @date 2024-04-09 + */ +public interface IOrderInfoService +{ + /** + * 查询订单基础信息 + * + * @param id 订单基础信息主键 + * @return 订单基础信息 + */ + public OrderInfo selectOrderInfoById(Long id); + + /** + * 查询订单基础信息列表 + * + * @param orderInfo 订单基础信息 + * @return 订单基础信息集合 + */ + public List selectOrderInfoList(OrderInfo orderInfo); + + /** + * 查询订单基础信息列表 + */ + List queryList(OrderListReqVo vo); + + /** + * 新增订单基础信息 + * + * @param orderInfo 订单基础信息 + * @return 结果 + */ + public Long createOrder(OrderCreateDto orderInfo); + + /** + * 修改订单基础信息 + * + * @param orderInfo 订单基础信息 + * @return 结果 + */ + public int updateOrderInfo(OrderInfo orderInfo); + + /** + * 批量删除订单基础信息 + * + * @param ids 需要删除的订单基础信息主键集合 + * @return 结果 + */ + public int deleteOrderInfoByIds(Long[] ids); + + /** + * 删除订单基础信息信息 + * + * @param id 订单基础信息主键 + * @return 结果 + */ + public int deleteOrderInfoById(Long id); + + void orderPay(Long orderId); + + OrderInfo selectOrderInfoByOrderNo(String orderNo); + + void insertOrder(OrderInfo orderInfo); + +} diff --git a/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java b/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java new file mode 100644 index 0000000..655d09e --- /dev/null +++ b/sf-order/src/main/java/com/sf/order/service/impl/OrderInfoServiceImpl.java @@ -0,0 +1,145 @@ +package com.sf.order.service.impl; + +import com.sf.common.exception.ServiceException; +import com.sf.common.utils.DateUtils; +import com.sf.common.utils.SnowflakeIdWorker; +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.mapper.OrderInfoMapper; +import com.sf.order.service.IOrderInfoService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 订单基础信息Service业务层处理 + * + * @author ztzh + * @date 2024-04-09 + */ +@Service +public class OrderInfoServiceImpl implements IOrderInfoService { + @Resource + private OrderInfoMapper orderInfoMapper; + @Resource + private SnowflakeIdWorker snowflakeIdWorker; + + /** + * 查询订单基础信息 + * + * @param id 订单基础信息主键 + * @return 订单基础信息 + */ + @Override + public OrderInfo selectOrderInfoById(Long id) { + return orderInfoMapper.selectOrderInfoById(id); + } + + /** + * 查询订单基础信息列表 + * + * @param orderInfo 订单基础信息 + * @return 订单基础信息 + */ + @Override + public List selectOrderInfoList(OrderInfo orderInfo) { + return orderInfoMapper.selectOrderInfoList(orderInfo); + } + + public List queryList(OrderListReqVo vo) { + return orderInfoMapper.queryList(vo); + } + + /** + * 新增订单基础信息 + * + * @param orderCreateDto 订单基础信息 + * @return 结果 + */ + @Override + public Long createOrder(OrderCreateDto orderCreateDto) { + OrderInfo orderInfo = new OrderInfo(); + long id = snowflakeIdWorker.nextId(); + orderInfo.setOrderNo(id); + orderInfo.setPayType(1L); + orderInfo.setReceiveType(0L); + orderInfo.setOrderStatus(0L); + orderInfo.setCreateUserId(orderCreateDto.getUserId()); + orderInfo.setUpdateUserId(orderCreateDto.getUserId()); + orderInfo.setGoodsId(orderCreateDto.getGoodsId()); + orderInfo.setOrderAmt(orderCreateDto.getAmount()); + orderInfo.setPayAmt(orderCreateDto.getAmount()); + orderInfo.setCreateTime(DateUtils.getNowDate()); + orderInfo.setUpdateTime(DateUtils.getNowDate()); + orderInfo.setOutOrderNo(orderCreateDto.getOutOrderNo()); + orderInfo.setAppCode(orderCreateDto.getAppCode()); + orderInfoMapper.insertOrderInfo(orderInfo); + return id; + } + + /** + * 修改订单基础信息 + * + * @param orderInfo 订单基础信息 + * @return 结果 + */ + @Override + public int updateOrderInfo(OrderInfo orderInfo) { + orderInfo.setUpdateTime(DateUtils.getNowDate()); + return orderInfoMapper.updateOrderInfo(orderInfo); + } + + @Override + public void orderPay(Long orderId) { + OrderInfo updateOrder = this.selectOrderInfoById(orderId); + if (updateOrder == null) { + throw new ServiceException("订单缺失!"); + } +// if (!OrderInfoConstant.ORDER_WAITING_FOR_PAYMENT.equals(orderInfo.getOrderStatus())) { +// throw new ServiceException("订单状态异常!"); +// } + updateOrder.setOrderStatus(1L); + updateOrder.setPayTime(DateUtils.getNowDate()); + // 修改订单状态 + 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); + } + + /** + * 批量删除订单基础信息 + * + * @param ids 需要删除的订单基础信息主键 + * @return 结果 + */ + @Override + public int deleteOrderInfoByIds(Long[] ids) { + return orderInfoMapper.deleteOrderInfoByIds(ids); + } + + /** + * 删除订单基础信息信息 + * + * @param id 订单基础信息主键 + * @return 结果 + */ + @Override + public int deleteOrderInfoById(Long id) { + return orderInfoMapper.deleteOrderInfoById(id); + } + +} diff --git a/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml b/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml new file mode 100644 index 0000000..3a208c6 --- /dev/null +++ b/sf-order/src/main/resources/mapper/order/OrderInfoMapper.xml @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 a.id,a.app_code,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 + + + + + + + + + + + insert into Order_info + + 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, + goods_count, + goods_type, + goods_price, + goods_code, + app_code, + + + #{id}, + #{orderNo}, + #{orderStatus}, + #{payType}, + #{payChannel}, + #{orderAmt}, + #{freightAmt}, + #{payAmt}, + #{reallyAmt}, + #{receiveType}, + #{goodsId}, + #{businessId}, + #{receiveAddrId}, + #{createTime}, + #{payTime}, + #{createUserId}, + #{updateUserId}, + #{isDelete}, + #{updateTime}, + #{trackNo}, + #{orderType}, + #{outOrderNo}, + #{payData}, + #{reductionAmout}, + #{goodsCount}, + #{goodsType}, + #{goodsPrice}, + #{goodsCode}, + #{appCode}, + + + + + update Order_info + + order_no = #{orderNo}, + order_status = #{orderStatus}, + pay_type = #{payType}, + pay_channel = #{payChannel}, + order_amt = #{orderAmt}, + freight_amt = #{freightAmt}, + pay_amt = #{payAmt}, + really_amt = #{reallyAmt}, + receive_type = #{receiveType}, + goods_id = #{goodsId}, + business_id = #{businessId}, + receive_addr_id = #{receiveAddrId}, + create_time = #{createTime}, + pay_time = #{payTime}, + create_user_id = #{createUserId}, + update_user_id = #{updateUserId}, + is_delete = #{isDelete}, + update_time = #{updateTime}, + track_no = #{trackNo}, + order_type = #{orderType}, + out_order_no = #{outOrderNo}, + pay_data = #{payData}, + reduction_amout = #{reductionAmout}, + + where id = #{id} + + + + delete from Order_info where id = #{id} + + + + delete from Order_info where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-payment/pom.xml b/sf-payment/pom.xml new file mode 100644 index 0000000..680816a --- /dev/null +++ b/sf-payment/pom.xml @@ -0,0 +1,50 @@ + + + + smarterFramework + com.smarterFramework + 1.0.0 + + 4.0.0 + + sf-payment + + + 支付模块 + + + + + + + com.smarterFramework + sf-common + + + com.smarterFramework + sf-framework + + + com.smarterFramework + sf-order + + + com.smarterFramework + sf-service + + + org.bouncycastle + bcprov-jdk18on + 1.73 + + + com.auth0 + java-jwt + 4.4.0 + + + + + \ No newline at end of file diff --git a/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java b/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java new file mode 100644 index 0000000..f9d76f5 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/config/HuaweiPaymentConfig.java @@ -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 { + + /** + * 客户端id:(oauth用) + */ + @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; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java b/sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java new file mode 100644 index 0000000..53b8b1c --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/constant/GoodsConstants.java @@ -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; + +} diff --git a/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java b/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java new file mode 100644 index 0000000..34eafee --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/controller/HuaweiPaymentController.java @@ -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(); + } + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPaymentCallback.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPaymentCallback.java new file mode 100644 index 0000000..b7e46d2 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPaymentCallback.java @@ -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; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java new file mode 100644 index 0000000..49a250c --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyDTO.java @@ -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; + + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyResponseDTO.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyResponseDTO.java new file mode 100644 index 0000000..c684a59 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiPurchasesVerifyResponseDTO.java @@ -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; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java new file mode 100644 index 0000000..4814e9f --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/HuaweiQueryResponse.java @@ -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; +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/InAppPurchaseData.java b/sf-payment/src/main/java/com/sf/payment/domain/InAppPurchaseData.java new file mode 100644 index 0000000..05f7e97 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/InAppPurchaseData.java @@ -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:用户签约信息异常 + * 4:Billing错误 + * 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; + + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java b/sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java new file mode 100644 index 0000000..8c8fab2 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/PurchaseOrderPayload.java @@ -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” 表示周期的单位是天(Days)。ISO 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分钟的时间段。 + */ +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java b/sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java new file mode 100644 index 0000000..7fcad45 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/SubGroupStatusPayload.java @@ -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切换B,B切换C, + * 此处是C的订阅状态。 + */ + private SubscriptionStatus lastSubscriptionStatus; + + /** + * 订阅组最近生效的 + * 历史订阅状态 + * SubscriptionStatus的列表,比如A切换B,B切换C,这里包含C,B,A三个订阅状态信息。 + */ + private List historySubscriptionStatusList; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java b/sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java new file mode 100644 index 0000000..cfff488 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/SubRenewalInfo.java @@ -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; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java b/sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java new file mode 100644 index 0000000..8adb949 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/domain/SubscriptionStatus.java @@ -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 recentPurchaseOrderList; + + /** + * 当前订阅最新的未来扣费计划,包含的参数请参见SubRenewalInfo。 + */ + private SubRenewalInfo renewalInfo; + + +} diff --git a/sf-payment/src/main/java/com/sf/payment/service/IHuaweiPaymentService.java b/sf-payment/src/main/java/com/sf/payment/service/IHuaweiPaymentService.java new file mode 100644 index 0000000..1383be3 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/service/IHuaweiPaymentService.java @@ -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); +} diff --git a/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java b/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java new file mode 100644 index 0000000..473d026 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/service/impl/HuaweiPaymentServiceImpl.java @@ -0,0 +1,403 @@ +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.enums.RequestHeaderEnums; +import com.sf.common.utils.DateUtils; +import com.sf.common.utils.SecurityUtils; +import com.sf.common.utils.SnowflakeIdWorker; +import com.sf.common.utils.http.RequestUtils; +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) { + // 验证 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 bodyMap = new HashMap<>(); + bodyMap.put("purchaseToken", lastPurchaseOrder.getPurchaseToken()); + bodyMap.put("purchaseOrderId", lastPurchaseOrder.getPurchaseOrderId()); + // construct the Authorization in Header + Map headers = buildAuthorization(huaweiPaymentConfig.getAppId(), bodyMap); + log.info("调用华为主动续期入惨:{}", JSON.toJSONString(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, subGroupStatusPayload.getLastSubscriptionStatus()); + } + + /** + * 发货 + * + * @param purchaseOrder + * @param huaweiQueryResponsePurchaseOrderPayload + * @param lastSubscriptionStatus + * @return + */ + private PurchaseOrderPayload delivery(PurchaseOrderPayload purchaseOrder, PurchaseOrderPayload huaweiQueryResponsePurchaseOrderPayload, SubscriptionStatus lastSubscriptionStatus) { + 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, lastSubscriptionStatus); + } 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 bodyMap = new HashMap<>(); + bodyMap.put("purchaseToken", appPurchaseOrderPayload.getPurchaseToken()); + bodyMap.put("purchaseOrderId", appPurchaseOrderPayload.getPurchaseOrderId()); + // construct the Authorization in Header + Map 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, null); + } + + private void createOrder(PurchaseOrderPayload appPurchaseOrderPayload, GoodsMessages goods) { + Long userId = SecurityUtils.getUserId(); + OrderInfo orderInfo = new OrderInfo(); + orderInfo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + orderInfo.setOrderNo(snowflakeIdWorker.nextId()); + orderInfo.setOrderStatus(4L); + 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()); + orderInfo.setCreateTime(DateUtils.getNowDate()); + orderInfo.setUpdateTime(DateUtils.getNowDate()); + orderInfoService.insertOrder(orderInfo); + + } + + private void distributeMembershipBenefits(PurchaseOrderPayload purchaseOrderPayload, SubscriptionStatus lastSubscriptionStatus) { + // 发放会员权益 + Long userId = SecurityUtils.getUserId(); + UserMember userMember = userMemberService.selectUserMemberByUserId(userId); + boolean isSubscription = GoodsConstants.GOODS_TYPE_AUTOMATIC_RENEWAL_SUBSCRIPTION.equals(purchaseOrderPayload.getProductType()); + int subscriptionStatus = isSubscription ? lastSubscriptionStatus.getStatus() : 0; + if (userMember == null) { + // 添加会员信息 + DateTime payTime = DateUtil.date(purchaseOrderPayload.getPurchaseTime()); + userMember = new UserMember(); + userMember.setMemberLevel(isSubscription ? 1 : 2); + userMember.setSubscriptionStatus(subscriptionStatus); + 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 { + // 更新 + userMember.setMemberLevel(isSubscription ? 1 : 2); + userMember.setSubscriptionStatus(subscriptionStatus); + 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 buildAuthorization(String appId, Map body) { + Map jwtHeader = new HashMap<>(8); + jwtHeader.put("alg", "ES256"); + jwtHeader.put("typ", "JWT"); + jwtHeader.put("kid", "0ae3e1be-374b-43a5-a297-045addbf76eb"); + Map 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 headers = new HashMap<>(); + headers.put("Authorization", authorization); + headers.put("Content-Type", "application/json; charset=UTF-8"); + return headers; + } + + private static String getJwtPayloadDigest(Map 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中的productId、price、currency等信息的一致性 + * + * @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中的productId、price、currency等信息的一致性 + checkRes = checkProductIdAndPriceAndCurrency(content, orderInfo); + } + return checkRes; + } + + /** + * 校验InAppPurchaseData中的productId、price、currency等信息的一致性 + * + * @param content 结果字符串 + * @param orderInfo 您的订单信息,包括productId、price、currency + * @return 是否校验通过 + */ + public static boolean checkProductIdAndPriceAndCurrency(String content, OrderInfo orderInfo) { + InAppPurchaseData inAppPurchaseData = JSON.parseObject(content, InAppPurchaseData.class); + // 校验InAppPurchaseData中的productId、price、currency等信息的一致性 + 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; + } + +} diff --git a/sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java b/sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java new file mode 100644 index 0000000..4572ea6 --- /dev/null +++ b/sf-payment/src/main/java/com/sf/payment/utils/HuaweiTokenGenerator.java @@ -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 jwtHeader, Map 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); + } + } +} diff --git a/sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 b/sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 new file mode 100644 index 0000000..e1ebbeb --- /dev/null +++ b/sf-payment/src/main/resources/IAPKey_0ae3e1be-374b-43a5-a297-045addbf76eb.p8 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQggG04243qynU/yWYy +XpYVy9ZWMuLKzZiwhXCBWQBCOLigCgYIKoZIzj0DAQehRANCAARNln2/d+TM2pIO +LWQzvI77gPAVEvVCSlIuiJ+J7CJSG5KCysBaEeiiD5cc4dZWnUBijF8FBh7nDLaH +VwFXfrS+ +-----END PRIVATE KEY----- diff --git a/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java b/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java new file mode 100644 index 0000000..624cca7 --- /dev/null +++ b/sf-service/src/main/java/com/sf/service/controller/GoodsMessagesController.java @@ -0,0 +1,110 @@ +package com.sf.service.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import apijson.orm.model.Request; +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.StringUtils; +import com.sf.common.utils.http.RequestUtils; +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; +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.common.utils.poi.ExcelUtil; +import com.sf.common.core.page.TableDataInfo; + +/** + * 商品信息Controller + * + * @author zoukun + * @date 2024-04-18 + */ +@RestController +@RequestMapping("/service/goods") +public class GoodsMessagesController extends BaseController +{ + @Autowired + private IGoodsMessagesService goodsMessagesService; + + /** + * 查询商品信息列表 + */ + @GetMapping("/list") + public TableDataInfo list(GoodsMessages goodsMessages) + { + goodsMessages.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + startPage(); + List list = goodsMessagesService.selectGoodsMessagesList(goodsMessages); + return getDataTable(list); + } + + /** + * 导出商品信息列表 + */ + @PreAuthorize("@ss.hasPermi('service:goods:export')") + @Log(title = "商品信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, GoodsMessages goodsMessages) + { + List list = goodsMessagesService.selectGoodsMessagesList(goodsMessages); + ExcelUtil util = new ExcelUtil(GoodsMessages.class); + util.exportExcel(response, list, "商品信息数据"); + } + + /** + * 获取商品信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('service:goods:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(goodsMessagesService.selectGoodsMessagesById(id)); + } + + /** + * 新增商品信息 + */ + @PreAuthorize("@ss.hasPermi('service:goods:add')") + @Log(title = "商品信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody GoodsMessages goodsMessages) + { + goodsMessages.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + return toAjax(goodsMessagesService.insertGoodsMessages(goodsMessages)); + } + + /** + * 修改商品信息 + */ + @PreAuthorize("@ss.hasPermi('service:goods:edit')") + @Log(title = "商品信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody GoodsMessages goodsMessages) + { + return toAjax(goodsMessagesService.updateGoodsMessages(goodsMessages)); + } + + /** + * 删除商品信息 + */ + @PreAuthorize("@ss.hasPermi('service:goods:remove')") + @Log(title = "商品信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(goodsMessagesService.deleteGoodsMessagesByIds(ids)); + } +} diff --git a/sf-service/src/main/java/com/sf/service/domain/GoodsMessages.java b/sf-service/src/main/java/com/sf/service/domain/GoodsMessages.java new file mode 100644 index 0000000..19f9b50 --- /dev/null +++ b/sf-service/src/main/java/com/sf/service/domain/GoodsMessages.java @@ -0,0 +1,254 @@ +package com.sf.service.domain; + +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; + +/** + * 商品信息对象 GOODS_MESSAGES + * + * @author zoukun + * @date 2024-04-18 + */ +public class GoodsMessages extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + /** 应用id */ + private String appCode; + + /** 商品编号 */ + @Excel(name = "商品编号") + private String goodsCode; + + /** 库存表编号 */ + private Long stockId; + + /** 审核状态,1通过,0未通过 */ + @Excel(name = "审核状态,1通过,0未通过") + private Long reviewStatus; + + /** 商品标题 */ + @Excel(name = "商品标题") + private String productTitle; + + /** 商品图片 */ + @Excel(name = "商品图片") + private String productPicture; + + /** 商品原价 */ + @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; + + /** 排序 */ + private Long orderNum; + + /** 逻辑删除,0:未删除,1:删除 */ + private Long isDelete; + + /** 创建人 */ + private String created; + + /** 更新人 */ + private String modified; + + /** 商品名称 */ + private String goodsName; + + /** 商品型号 */ + @Excel(name = "商品型号") + private String goodsModel; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setGoodsCode(String goodsCode) + { + this.goodsCode = goodsCode; + } + + public String getGoodsCode() + { + return goodsCode; + } + public void setStockId(Long stockId) + { + this.stockId = stockId; + } + + public Long getStockId() + { + return stockId; + } + public void setReviewStatus(Long reviewStatus) + { + this.reviewStatus = reviewStatus; + } + + public Long getReviewStatus() + { + return reviewStatus; + } + public void setProductTitle(String productTitle) + { + this.productTitle = productTitle; + } + + public String getProductTitle() + { + return productTitle; + } + public void setProductPicture(String productPicture) + { + this.productPicture = productPicture; + } + + public String getProductPicture() + { + return productPicture; + } + public void setOriginalPrice(Long originalPrice) + { + this.originalPrice = originalPrice; + } + + public Long getOriginalPrice() + { + return originalPrice; + } + public void setProductDesc(String productDesc) + { + this.productDesc = productDesc; + } + + public String getProductDesc() + { + 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; + } + + public Long getOrderNum() + { + return orderNum; + } + public void setIsDelete(Long isDelete) + { + this.isDelete = isDelete; + } + + public Long getIsDelete() + { + return isDelete; + } + public void setCreated(String created) + { + this.created = created; + } + + public String getCreated() + { + return created; + } + public void setModified(String modified) + { + this.modified = modified; + } + + public String getModified() + { + return modified; + } + public void setGoodsName(String goodsName) + { + this.goodsName = goodsName; + } + + public String getGoodsName() + { + return goodsName; + } + public void setGoodsModel(String goodsModel) + { + this.goodsModel = goodsModel; + } + + public String getGoodsModel() + { + return goodsModel; + } + + public String getAppCode() { + return appCode; + } + + public void setAppCode(String appCode) { + this.appCode = appCode; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("appCode", getAppCode()) + .append("goodsCode", getGoodsCode()) + .append("stockId", getStockId()) + .append("reviewStatus", getReviewStatus()) + .append("productTitle", getProductTitle()) + .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("goodsName", getGoodsName()) + .append("goodsModel", getGoodsModel()) + .toString(); + } +} diff --git a/sf-service/src/main/java/com/sf/service/mapper/GoodsMessagesMapper.java b/sf-service/src/main/java/com/sf/service/mapper/GoodsMessagesMapper.java new file mode 100644 index 0000000..02a2f1a --- /dev/null +++ b/sf-service/src/main/java/com/sf/service/mapper/GoodsMessagesMapper.java @@ -0,0 +1,63 @@ +package com.sf.service.mapper; + +import java.util.List; +import com.sf.service.domain.GoodsMessages; + +/** + * 商品信息Mapper接口 + * + * @author zoukun + * @date 2024-04-18 + */ +public interface GoodsMessagesMapper +{ + /** + * 查询商品信息 + * + * @param id 商品信息主键 + * @return 商品信息 + */ + public GoodsMessages selectGoodsMessagesById(Long id); + + /** + * 查询商品信息列表 + * + * @param goodsMessages 商品信息 + * @return 商品信息集合 + */ + public List selectGoodsMessagesList(GoodsMessages goodsMessages); + + /** + * 新增商品信息 + * + * @param goodsMessages 商品信息 + * @return 结果 + */ + public int insertGoodsMessages(GoodsMessages goodsMessages); + + /** + * 修改商品信息 + * + * @param goodsMessages 商品信息 + * @return 结果 + */ + public int updateGoodsMessages(GoodsMessages goodsMessages); + + /** + * 删除商品信息 + * + * @param id 商品信息主键 + * @return 结果 + */ + public int deleteGoodsMessagesById(Long id); + + /** + * 批量删除商品信息 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteGoodsMessagesByIds(Long[] ids); + + GoodsMessages selectGoodsMessagesByCode(String goodsCode); +} diff --git a/sf-service/src/main/java/com/sf/service/service/IGoodsMessagesService.java b/sf-service/src/main/java/com/sf/service/service/IGoodsMessagesService.java new file mode 100644 index 0000000..29f7dab --- /dev/null +++ b/sf-service/src/main/java/com/sf/service/service/IGoodsMessagesService.java @@ -0,0 +1,63 @@ +package com.sf.service.service; + +import java.util.List; +import com.sf.service.domain.GoodsMessages; + +/** + * 商品信息Service接口 + * + * @author zoukun + * @date 2024-04-18 + */ +public interface IGoodsMessagesService +{ + /** + * 查询商品信息 + * + * @param id 商品信息主键 + * @return 商品信息 + */ + public GoodsMessages selectGoodsMessagesById(Long id); + + /** + * 查询商品信息列表 + * + * @param goodsMessages 商品信息 + * @return 商品信息集合 + */ + public List selectGoodsMessagesList(GoodsMessages goodsMessages); + + /** + * 新增商品信息 + * + * @param goodsMessages 商品信息 + * @return 结果 + */ + public int insertGoodsMessages(GoodsMessages goodsMessages); + + /** + * 修改商品信息 + * + * @param goodsMessages 商品信息 + * @return 结果 + */ + public int updateGoodsMessages(GoodsMessages goodsMessages); + + /** + * 批量删除商品信息 + * + * @param ids 需要删除的商品信息主键集合 + * @return 结果 + */ + public int deleteGoodsMessagesByIds(Long[] ids); + + /** + * 删除商品信息信息 + * + * @param id 商品信息主键 + * @return 结果 + */ + public int deleteGoodsMessagesById(Long id); + + GoodsMessages selectGoodsMessagesByCode(String goodsCode); +} diff --git a/sf-service/src/main/java/com/sf/service/service/impl/GoodsMessagesServiceImpl.java b/sf-service/src/main/java/com/sf/service/service/impl/GoodsMessagesServiceImpl.java new file mode 100644 index 0000000..0541ba8 --- /dev/null +++ b/sf-service/src/main/java/com/sf/service/service/impl/GoodsMessagesServiceImpl.java @@ -0,0 +1,105 @@ +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.service.mapper.GoodsMessagesMapper; +import com.sf.service.domain.GoodsMessages; +import com.sf.service.service.IGoodsMessagesService; +import org.springframework.util.StringUtils; + +/** + * 商品信息Service业务层处理 + * + * @author zoukun + * @date 2024-04-18 + */ +@Service +public class GoodsMessagesServiceImpl implements IGoodsMessagesService +{ + @Autowired + private GoodsMessagesMapper goodsMessagesMapper; + + /** + * 查询商品信息 + * + * @param id 商品信息主键 + * @return 商品信息 + */ + @Override + public GoodsMessages selectGoodsMessagesById(Long id) + { + return goodsMessagesMapper.selectGoodsMessagesById(id); + } + + /** + * 查询商品信息列表 + * + * @param goodsMessages 商品信息 + * @return 商品信息 + */ + @Override + public List selectGoodsMessagesList(GoodsMessages goodsMessages) + { + return goodsMessagesMapper.selectGoodsMessagesList(goodsMessages); + } + + /** + * 新增商品信息 + * + * @param goodsMessages 商品信息 + * @return 结果 + */ + @Override + public int insertGoodsMessages(GoodsMessages goodsMessages) + { + goodsMessages.setCreateTime(DateUtils.getNowDate()); + return goodsMessagesMapper.insertGoodsMessages(goodsMessages); + } + + /** + * 修改商品信息 + * + * @param goodsMessages 商品信息 + * @return 结果 + */ + @Override + public int updateGoodsMessages(GoodsMessages goodsMessages) + { + goodsMessages.setUpdateTime(DateUtils.getNowDate()); + return goodsMessagesMapper.updateGoodsMessages(goodsMessages); + } + + /** + * 批量删除商品信息 + * + * @param ids 需要删除的商品信息主键 + * @return 结果 + */ + @Override + public int deleteGoodsMessagesByIds(Long[] ids) + { + return goodsMessagesMapper.deleteGoodsMessagesByIds(ids); + } + + /** + * 删除商品信息信息 + * + * @param id 商品信息主键 + * @return 结果 + */ + @Override + public int deleteGoodsMessagesById(Long id) + { + return goodsMessagesMapper.deleteGoodsMessagesById(id); + } + + @Override + public GoodsMessages selectGoodsMessagesByCode(String goodsCode) { + if (StringUtils.hasText(goodsCode)){ + return goodsMessagesMapper.selectGoodsMessagesByCode(goodsCode); + } + return null; + } +} diff --git a/sf-service/src/main/resources/mapper/service/GoodsMessagesMapper.xml b/sf-service/src/main/resources/mapper/service/GoodsMessagesMapper.xml new file mode 100644 index 0000000..d3037a7 --- /dev/null +++ b/sf-service/src/main/resources/mapper/service/GoodsMessagesMapper.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, app_code, 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 + + + + + + + + + insert into GOODS_MESSAGES + + app_code, + 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, + + + #{appCode}, + #{goodsCode}, + #{stockId}, + #{reviewStatus}, + #{productTitle}, + #{productPicture}, + #{originalPrice}, + #{productDesc}, + #{goodsType}, + #{goodsSpec}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + #{goodsName}, + #{goodsModel}, + + + + + update GOODS_MESSAGES + + goods_code = #{goodsCode}, + stock_id = #{stockId}, + review_status = #{reviewStatus}, + product_title = #{productTitle}, + product_picture = #{productPicture}, + original_price = #{originalPrice}, + product_desc = #{productDesc}, + goods_type = #{goodsType}, + goods_spec = #{goodsSpec}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + goods_name = #{goodsName}, + goods_model = #{goodsModel}, + + where id = #{id} + + + + delete from GOODS_MESSAGES where id = #{id} + + + + delete from GOODS_MESSAGES where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-system/pom.xml b/sf-system/pom.xml index 1a80595..d7d76c6 100644 --- a/sf-system/pom.xml +++ b/sf-system/pom.xml @@ -22,6 +22,10 @@ com.smarterFramework sf-common + + com.smarterFramework + sf-file + diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java index 59f41e6..53b31fa 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentApplyEnvironmentController.java @@ -1,28 +1,35 @@ package com.sf.system.deployment.controller; import java.util.List; - +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestAttributeEvent; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.StringUtils; +import com.sf.common.utils.http.RequestUtils; +import com.sf.system.deployment.domain.DeploymentApplyEnvironment; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; 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.core.page.TableDataInfo; import com.sf.common.enums.BusinessType; +import com.sf.deployment.service.IDeploymentApplyEnvironmentService; import com.sf.common.utils.poi.ExcelUtil; -import com.sf.system.deployment.domain.DeploymentApplyEnvironment; -import com.sf.system.deployment.service.IDeploymentApplyEnvironmentService; +import com.sf.common.core.page.TableDataInfo; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; /** * 环境维护Controller @@ -44,6 +51,7 @@ public class DeploymentApplyEnvironmentController extends BaseController @GetMapping("/list") public TableDataInfo list(DeploymentApplyEnvironment deploymentApplyEnvironment) { + deploymentApplyEnvironment.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); startPage(); List list = deploymentApplyEnvironmentService.selectDeploymentApplyEnvironmentList(deploymentApplyEnvironment); return getDataTable(list); @@ -80,6 +88,7 @@ public class DeploymentApplyEnvironmentController extends BaseController @PostMapping public AjaxResult add(@RequestBody DeploymentApplyEnvironment deploymentApplyEnvironment) { + deploymentApplyEnvironment.setAppCode( RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); return toAjax(deploymentApplyEnvironmentService.insertDeploymentApplyEnvironment(deploymentApplyEnvironment)); } diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentModuleListController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentModuleListController.java new file mode 100644 index 0000000..fbcdc89 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentModuleListController.java @@ -0,0 +1,162 @@ +package com.sf.system.deployment.controller; + +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletResponse; + +import com.sf.common.core.redis.RedisCache; +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.http.RequestUtils; +import com.sf.common.utils.openssl.AES256Util; +import com.sf.common.utils.uuid.UUID; +import com.sf.system.deployment.domain.rqs.ModuleListByCoreRequest; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FileCopyUtils; +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.deployment.domain.DeploymentModuleList; +import com.sf.system.deployment.service.IDeploymentModuleListService; +import com.sf.common.utils.poi.ExcelUtil; +import com.sf.common.core.page.TableDataInfo; + +/** + * H5模块包Controller + * + * @author ztzh + * @date 2024-04-22 + */ +@RestController +@RequestMapping("/deployment/module") +public class DeploymentModuleListController extends BaseController { + @Autowired + private IDeploymentModuleListService deploymentModuleListService; + @Autowired + private RedisCache redisCache; + + /** + * 查询H5模块包列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:module:list')") + @GetMapping("/list") + public TableDataInfo list(DeploymentModuleList deploymentModuleList) { + deploymentModuleList.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + startPage(); + List list = deploymentModuleListService.selectDeploymentModuleListList(deploymentModuleList); + return getDataTable(list); + } + + /** + * 查询H5模块包列表 + */ + @GetMapping("/qurey/list") + public AjaxResult queryByCore(ModuleListByCoreRequest request) { + DeploymentModuleList deploymentModuleList = new DeploymentModuleList(); + deploymentModuleList.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + deploymentModuleList.setSysType(request.getSysType()); + List list = deploymentModuleListService.selectDeploymentModuleListList(deploymentModuleList); + return success(list); + } + + /** + * 查询模块包列表 + */ + @GetMapping("/qurey/file") + public AjaxResult queryByCore(ModuleListByCoreRequest request, HttpServletResponse response) { + DeploymentModuleList deploymentModuleList = new DeploymentModuleList(); + deploymentModuleList.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + String authorization = RequestUtils.getHeader(RequestHeaderEnums.AUTHORIZATION.getCode()); + deploymentModuleList.setSysType(request.getSysType()); + deploymentModuleList.setModuleType(request.getModuleType()); + List list = deploymentModuleListService.selectDeploymentModuleListList(deploymentModuleList); + if (list.isEmpty() && null == list.get(0)) { + throw new SecurityException("无可用模块包"); + } + try { + String password = UUID.randomUUID().toString().trim().replaceAll("-", ""); + //加密 + InputStream inputStream1 = AES256Util.encryptFile(password, new URL(list.get(0).getModuleUrl()).openStream()); + FileCopyUtils.copy(inputStream1, response.getOutputStream()); + redisCache.setCacheObject(authorization + request.getModuleType(), password, 3000, TimeUnit.MINUTES); + } catch (Exception e) { + throw new RuntimeException(e); + } + return success(null); + } + + /** + * 获取压缩包密码 + */ + @GetMapping("/qurey/ZipCore") + public AjaxResult queryZipCore(ModuleListByCoreRequest request) { + String authorization = RequestUtils.getHeader(RequestHeaderEnums.AUTHORIZATION.getCode()); + String cacheObject = redisCache.getCacheObject(authorization + request.getModuleType()); + return success(UUID.randomUUID().toString().trim().replaceAll("-", "") + cacheObject + UUID.randomUUID().toString().trim().replaceAll("-", "")) + ; + } + + + /** + * 导出H5模块包列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:module:export')") + @Log(title = "H5模块包", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DeploymentModuleList deploymentModuleList) { + List list = deploymentModuleListService.selectDeploymentModuleListList(deploymentModuleList); + ExcelUtil util = new ExcelUtil(DeploymentModuleList.class); + util.exportExcel(response, list, "H5模块包数据"); + } + + /** + * 获取H5模块包详细信息 + */ + @PreAuthorize("@ss.hasPermi('deployment:module:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) { + return success(deploymentModuleListService.selectDeploymentModuleListById(id)); + } + + /** + * 新增H5模块包 + */ + @PreAuthorize("@ss.hasPermi('deployment:module:add')") + @Log(title = "H5模块包", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DeploymentModuleList deploymentModuleList) { + deploymentModuleList.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + return toAjax(deploymentModuleListService.insertDeploymentModuleList(deploymentModuleList)); + } + + /** + * 修改H5模块包 + */ + @PreAuthorize("@ss.hasPermi('deployment:module:edit')") + @Log(title = "H5模块包", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DeploymentModuleList deploymentModuleList) { + return toAjax(deploymentModuleListService.updateDeploymentModuleList(deploymentModuleList)); + } + + /** + * 删除H5模块包 + */ + @PreAuthorize("@ss.hasPermi('deployment:module:remove')") + @Log(title = "H5模块包", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) { + return toAjax(deploymentModuleListService.deleteDeploymentModuleListByIds(ids)); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentServicePublishController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentServicePublishController.java index 12d0459..0dd8fa3 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentServicePublishController.java +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentServicePublishController.java @@ -3,10 +3,15 @@ package com.sf.system.deployment.controller; import java.util.List; import javax.servlet.http.HttpServletResponse; +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.DateUtils; +import com.sf.common.utils.StringUtils; +import com.sf.common.utils.http.RequestUtils; import com.sf.system.deployment.domain.DeploymentServicePublish; import com.sf.system.deployment.service.IDeploymentServicePublishService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.unit.DataUnit; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -42,6 +47,7 @@ public class DeploymentServicePublishController extends BaseController @GetMapping("/list") public TableDataInfo list(DeploymentServicePublish deploymentServicePublish) { + deploymentServicePublish.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); startPage(); List list = deploymentServicePublishService.selectDeploymentServicePublishList(deploymentServicePublish); return getDataTable(list); @@ -78,6 +84,7 @@ public class DeploymentServicePublishController extends BaseController @PostMapping public AjaxResult add(@RequestBody DeploymentServicePublish deploymentServicePublish) { + deploymentServicePublish.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); return toAjax(deploymentServicePublishService.insertDeploymentServicePublish(deploymentServicePublish)); } @@ -102,4 +109,30 @@ public class DeploymentServicePublishController extends BaseController { return toAjax(deploymentServicePublishService.deleteDeploymentServicePublishByIds(ids)); } + + /** + * 发布功能 + */ + @PreAuthorize("@ss.hasPermi('deployment:publish:publish')") + @GetMapping(value = "/publish/{id}") + public AjaxResult publish(@PathVariable("id") Long id) throws Exception { + DeploymentServicePublish deploymentServicePublish = deploymentServicePublishService.selectDeploymentServicePublishById(id); +// deploymentServicePublish.setUpdateBy(getUsername()); + deploymentServicePublish.setUpdateTime(DateUtils.getNowDate()); + return success(deploymentServicePublishService.publish(deploymentServicePublish)); + } + + /** + * 下架 + */ + @PreAuthorize("@ss.hasPermi('deployment:publish:sold:out')") + @GetMapping(value = "/sold/out/{id}") + public AjaxResult soldOut(@PathVariable("id") Long id) + { + DeploymentServicePublish deploymentServicePublish = deploymentServicePublishService.selectDeploymentServicePublishById(id); +// deploymentServicePublish.setUpdateBy(getUsername()); + deploymentServicePublish.setUpdateTime(DateUtils.getNowDate()); + return success(deploymentServicePublishService.soldOut(deploymentServicePublish)); + } + } diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentWhitelistInfoController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentWhitelistInfoController.java new file mode 100644 index 0000000..8619710 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentWhitelistInfoController.java @@ -0,0 +1,128 @@ +package com.sf.system.deployment.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.http.RequestUtils; +import com.sf.system.deployment.domain.DeploymentWhitelistInfo; +import com.sf.system.deployment.service.IDeploymentWhitelistInfoService; +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.common.utils.poi.ExcelUtil; +import com.sf.common.core.page.TableDataInfo; + +/** + * 白名单列表Controller + * + * @author ztzh + * @date 2024-05-06 + */ +@RestController +@RequestMapping("/deployment/whitelist") +public class DeploymentWhitelistInfoController extends BaseController +{ + @Autowired + private IDeploymentWhitelistInfoService deploymentWhitelistInfoService; + + /** + * 查询白名单列表列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:list')") + @GetMapping("/list") + public TableDataInfo list(DeploymentWhitelistInfo deploymentWhitelistInfo) + { + deploymentWhitelistInfo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + startPage(); + List list = deploymentWhitelistInfoService.selectDeploymentWhitelistInfoList(deploymentWhitelistInfo); + return getDataTable(list); + } + + /** + * 导出白名单列表列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:export')") + @Log(title = "白名单列表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DeploymentWhitelistInfo deploymentWhitelistInfo) + { + deploymentWhitelistInfo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + List list = deploymentWhitelistInfoService.selectDeploymentWhitelistInfoList(deploymentWhitelistInfo); + ExcelUtil util = new ExcelUtil(DeploymentWhitelistInfo.class); + util.exportExcel(response, list, "白名单列表数据"); + } + + /** + * 获取白名单列表详细信息 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(deploymentWhitelistInfoService.selectDeploymentWhitelistInfoById(id)); + } + + /** + * 新增白名单列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:add')") + @Log(title = "白名单列表", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DeploymentWhitelistInfo deploymentWhitelistInfo) + { + deploymentWhitelistInfo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + return toAjax(deploymentWhitelistInfoService.insertDeploymentWhitelistInfo(deploymentWhitelistInfo)); + } + + /** + * 修改白名单列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:edit')") + @Log(title = "白名单列表", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DeploymentWhitelistInfo deploymentWhitelistInfo) + { + return toAjax(deploymentWhitelistInfoService.updateDeploymentWhitelistInfo(deploymentWhitelistInfo)); + } + + /** + * 删除白名单列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:remove')") + @Log(title = "白名单列表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(deploymentWhitelistInfoService.deleteDeploymentWhitelistInfoByIds(ids)); + } + /** + * 发布功能 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:publish')") + @GetMapping(value = "/publish/{id}") + public AjaxResult publish(@PathVariable("id") Long id) throws Exception { + return success(deploymentWhitelistInfoService.publish(id)); + } + /** + * 下架功能 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelist:sold:out')") + @GetMapping(value = "/sold/out/{id}") + public AjaxResult soldOut(@PathVariable("id") Long id) + { + return success(deploymentWhitelistInfoService.soldOut(id)); + } + +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentWhitelistListController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentWhitelistListController.java new file mode 100644 index 0000000..0f0c786 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/DeploymentWhitelistListController.java @@ -0,0 +1,121 @@ +package com.sf.system.deployment.controller; + +import java.io.IOException; +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import cn.hutool.core.util.ObjectUtil; +import com.sf.common.exception.ServiceException; +import com.sf.system.deployment.domain.DeploymentWhitelistList; +import com.sf.system.deployment.service.IDeploymentWhitelistListService; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +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.common.utils.poi.ExcelUtil; +import com.sf.common.core.page.TableDataInfo; +import org.springframework.web.multipart.MultipartFile; + +/** + * 白名单成员列Controller + * + * @author ztzh + * @date 2024-05-06 + */ +@RestController +@RequestMapping("/deployment/whitelistMember") +public class DeploymentWhitelistListController extends BaseController +{ + @Autowired + private IDeploymentWhitelistListService deploymentWhitelistListService; + + /** + * 查询白名单成员列列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:list')") + @GetMapping("/list") + public TableDataInfo list(DeploymentWhitelistList deploymentWhitelistList) + { + if(null == deploymentWhitelistList.getWhitelistId()){ + throw new SecurityException("参数异常WhitelistId为空"); + } + startPage(); + List list = deploymentWhitelistListService.selectDeploymentWhitelistListList(deploymentWhitelistList); + return getDataTable(list); + } + + /** + * 导出白名单成员列列表 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:export')") + @Log(title = "白名单成员列", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DeploymentWhitelistList deploymentWhitelistList) + { + List list = deploymentWhitelistListService.selectDeploymentWhitelistListList(deploymentWhitelistList); + ExcelUtil util = new ExcelUtil(DeploymentWhitelistList.class); + util.exportExcel(response, list, "白名单成员列数据"); + } + + /** + * 获取白名单成员列详细信息 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(deploymentWhitelistListService.selectDeploymentWhitelistListById(id)); + } + + /** + * 新增白名单成员列 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:add')") + @Log(title = "白名单成员列", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DeploymentWhitelistList deploymentWhitelistList) + { + return toAjax(deploymentWhitelistListService.insertDeploymentWhitelistList(deploymentWhitelistList,getUsername())); + } + + /** + * 修改白名单成员列 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:edit')") + @Log(title = "白名单成员列", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DeploymentWhitelistList deploymentWhitelistList) + { + return toAjax(deploymentWhitelistListService.updateDeploymentWhitelistList(deploymentWhitelistList)); + } + + /** + * 删除白名单成员列 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:remove')") + @Log(title = "白名单成员列", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(deploymentWhitelistListService.deleteDeploymentWhitelistListByIds(ids)); + } + /** + * 批量导入 + * + * @param file 文件 + */ + @PreAuthorize("@ss.hasPermi('deployment:whitelistMember:batch:import')") + @Log(title = "批量上传", businessType = BusinessType.INSERT) + @PostMapping(value = "/batch/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public AjaxResult batchImport(@RequestPart("file") MultipartFile file,Long whitelistId) throws IOException { + if (ObjectUtil.isNull(file)) { + throw new ServiceException("上传文件不能为空"); + } + + return toAjax(deploymentWhitelistListService.batchImport(file,whitelistId,getUsername())); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/controller/SysApkInfoController.java b/sf-system/src/main/java/com/sf/system/deployment/controller/SysApkInfoController.java new file mode 100644 index 0000000..8d3561a --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/controller/SysApkInfoController.java @@ -0,0 +1,106 @@ +package com.sf.system.deployment.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.page.TableDataInfo; +import com.sf.common.enums.BusinessType; +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.StringUtils; +import com.sf.common.utils.http.RequestUtils; +import com.sf.common.utils.poi.ExcelUtil; +import com.sf.system.deployment.domain.SysApkInfo; +import com.sf.system.deployment.service.ISysApkInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 安装包管理(新)Controller + * + * @author ztzh + * @date 2024-04-11 + */ +@RestController +@RequestMapping("/deploy/INFO") +public class SysApkInfoController extends BaseController +{ + @Autowired + private ISysApkInfoService sysApkInfoService; + + /** + * 查询安装包管理(新)列表 + */ + @PreAuthorize("@ss.hasPermi('deploy:INFO:list')") + @GetMapping("/list") + public TableDataInfo list(SysApkInfo sysApkInfo) + { + sysApkInfo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + startPage(); + List list = sysApkInfoService.selectSysApkInfoList(sysApkInfo); + return getDataTable(list); + } + + /** + * 导出安装包管理(新)列表 + */ + @PreAuthorize("@ss.hasPermi('deploy:INFO:export')") + @Log(title = "安装包管理(新)", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysApkInfo sysApkInfo) + { + List list = sysApkInfoService.selectSysApkInfoList(sysApkInfo); + ExcelUtil util = new ExcelUtil(SysApkInfo.class); + util.exportExcel(response, list, "安装包管理(新)数据"); + } + + /** + * 获取安装包管理(新)详细信息 + */ + @PreAuthorize("@ss.hasPermi('deploy:INFO:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(sysApkInfoService.selectSysApkInfoById(id)); + } + + /** + * 新增安装包管理(新) + */ + @PreAuthorize("@ss.hasPermi('deploy:INFO:add')") + @Log(title = "安装包管理(新)", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysApkInfo sysApkInfo) + { + sysApkInfo.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + sysApkInfo.setCreated(getUsername()); + sysApkInfo.setModified(getUsername()); + return toAjax(sysApkInfoService.insertSysApkInfo(sysApkInfo)); + } + + /** + * 修改安装包管理(新) + */ + @PreAuthorize("@ss.hasPermi('deploy:INFO:edit')") + @Log(title = "安装包管理(新)", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysApkInfo sysApkInfo) + { + sysApkInfo.setModified(getUsername()); + return toAjax(sysApkInfoService.updateSysApkInfo(sysApkInfo)); + } + + /** + * 删除安装包管理(新) + */ + @PreAuthorize("@ss.hasPermi('deploy:INFO:remove')") + @Log(title = "安装包管理(新)", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(sysApkInfoService.deleteSysApkInfoByIds(ids)); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentApplyEnvironment.java b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentApplyEnvironment.java index 6531a20..7e2872d 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentApplyEnvironment.java +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentApplyEnvironment.java @@ -22,7 +22,7 @@ public class DeploymentApplyEnvironment extends BaseEntity private Long appId; /** 应用编号 */ - private String applyCode; + private String appCode; /** 环境名称 */ @Excel(name = "环境名称") @@ -58,14 +58,14 @@ public class DeploymentApplyEnvironment extends BaseEntity { return appId; } - public void setApplyCode(String applyCode) + public void setAppCode(String appCode) { - this.applyCode = applyCode; + this.appCode = appCode; } - public String getApplyCode() + public String getAppCode() { - return applyCode; + return appCode; } public void setName(String name) { @@ -109,7 +109,7 @@ public class DeploymentApplyEnvironment extends BaseEntity return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) .append("id", getId()) .append("appId", getAppId()) - .append("applyCode", getApplyCode()) + .append("applyCode", getAppCode()) .append("name", getName()) .append("serverAddress", getServerAddress()) .append("remark", getRemark()) diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentModuleList.java b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentModuleList.java new file mode 100644 index 0000000..4231824 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentModuleList.java @@ -0,0 +1,241 @@ +package com.sf.system.deployment.domain; + +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; + +/** + * H5模块包对象 DEPLOYMENT_MODULE_LIST + * + * @author ztzh + * @date 2024-04-22 + */ +public class DeploymentModuleList extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 应用id */ + private Long appId; + + /** 应用core */ + private String appCode; + + /** 上传类型 */ + private String uploadingType; + + /** 模块包名称名称 */ + @Excel(name = "模块包名称名称") + private String moduleName; + + /** 版本号 */ + @Excel(name = "版本号") + private String version; + + /** 安装包 */ + @Excel(name = "安装包") + private String moduleUrl; + + /** 安装包大小 */ + @Excel(name = "安装包大小") + private String moduleSize; + + /** 系统类型 */ + @Excel(name = "系统类型") + private String sysType; + + /** 模块类型 */ + @Excel(name = "模块类型") + private String moduleType; + + /** 上传状态 */ + @Excel(name = "上传状态") + private String uploadingStatus; + + /** 日志id */ + private Long uploadingLogId; + + /** 排序 */ + private Long orderNum; + + /** 逻辑删除,0:未删除,1:删除 */ + private Long isDelete; + + /** 创建人 */ + private String created; + + /** 更新人 */ + private String modified; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setAppId(Long appId) + { + this.appId = appId; + } + + public Long getAppId() + { + return appId; + } + public void setAppCode(String appCode) + { + this.appCode = appCode; + } + + public String getAppCode() + { + return appCode; + } + public void setUploadingType(String uploadingType) + { + this.uploadingType = uploadingType; + } + + public String getUploadingType() + { + return uploadingType; + } + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getModuleName() + { + return moduleName; + } + public void setVersion(String version) + { + this.version = version; + } + + public String getVersion() + { + return version; + } + public void setModuleUrl(String moduleUrl) + { + this.moduleUrl = moduleUrl; + } + + public String getModuleUrl() + { + return moduleUrl; + } + public void setModuleSize(String moduleSize) + { + this.moduleSize = moduleSize; + } + + public String getModuleSize() + { + return moduleSize; + } + public void setSysType(String sysType) + { + this.sysType = sysType; + } + + public String getSysType() + { + return sysType; + } + public void setModuleType(String moduleType) + { + this.moduleType = moduleType; + } + + public String getModuleType() + { + return moduleType; + } + public void setUploadingStatus(String uploadingStatus) + { + this.uploadingStatus = uploadingStatus; + } + + public String getUploadingStatus() + { + return uploadingStatus; + } + public void setUploadingLogId(Long uploadingLogId) + { + this.uploadingLogId = uploadingLogId; + } + + public Long getUploadingLogId() + { + return uploadingLogId; + } + public void setOrderNum(Long orderNum) + { + this.orderNum = orderNum; + } + + public Long getOrderNum() + { + return orderNum; + } + public void setIsDelete(Long isDelete) + { + this.isDelete = isDelete; + } + + public Long getIsDelete() + { + return isDelete; + } + public void setCreated(String created) + { + this.created = created; + } + + public String getCreated() + { + return created; + } + public void setModified(String modified) + { + this.modified = modified; + } + + public String getModified() + { + return modified; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("appId", getAppId()) + .append("appCore", getAppCode()) + .append("uploadingType", getUploadingType()) + .append("moduleName", getModuleName()) + .append("version", getVersion()) + .append("moduleUrl", getModuleUrl()) + .append("moduleSize", getModuleSize()) + .append("sysType", getSysType()) + .append("moduleType", getModuleType()) + .append("uploadingStatus", getUploadingStatus()) + .append("uploadingLogId", getUploadingLogId()) + .append("orderNum", getOrderNum()) + .append("isDelete", getIsDelete()) + .append("created", getCreated()) + .append("modified", getModified()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentServicePublish.java b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentServicePublish.java index 8dd44a9..716094f 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentServicePublish.java +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentServicePublish.java @@ -34,7 +34,7 @@ public class DeploymentServicePublish extends BaseEntity /** 安装包编号 */ @Excel(name = "安装包编号") - private String apkId; + private Long apkId; /** app store地址 */ @Excel(name = "app store地址") @@ -99,6 +99,41 @@ public class DeploymentServicePublish extends BaseEntity /** 更新人 */ private String modified; + /** 系统编码 */ + private String appCode; + + + /** 系统编码 */ + private String apkUrl; + + + /** 二维码 */ + private String quickMark; + + public String getApkUrl() { + return apkUrl; + } + + public void setApkUrl(String apkUrl) { + this.apkUrl = apkUrl; + } + + public String getQuickMark() { + return quickMark; + } + + public void setQuickMark(String quickMark) { + this.quickMark = quickMark; + } + + public String getAppCode() { + return appCode; + } + + public void setAppCode(String appCode) { + this.appCode = appCode; + } + public void setId(Long id) { this.id = id; @@ -135,12 +170,12 @@ public class DeploymentServicePublish extends BaseEntity { return systemType; } - public void setApkId(String apkId) + public void setApkId(Long apkId) { this.apkId = apkId; } - public String getApkId() + public Long getApkId() { return apkId; } @@ -315,7 +350,9 @@ public class DeploymentServicePublish extends BaseEntity .append("created", getCreated()) .append("modified", getModified()) .append("createTime", getCreateTime()) - .append("updateTime", getUpdateTime()) + .append("appCode", getAppCode()) + .append("quickMark", getQuickMark()) + .append("apkUrl", getApkUrl()) .toString(); } } diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentWhitelistInfo.java b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentWhitelistInfo.java new file mode 100644 index 0000000..c515aaf --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentWhitelistInfo.java @@ -0,0 +1,95 @@ +package com.sf.system.deployment.domain; + +import java.util.Date; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +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; + +/** + * 白名单列表对象 DEPLOYMENT_WHITELIST_INFO + * + * @author ztzh + * @date 2024-05-06 + */ +@Data +public class DeploymentWhitelistInfo extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 应用id */ + private Long appId; + + /** 应用id */ + @Excel(name = "应用id") + private String appCode; + + /** 白名单名称 */ + @Excel(name = "白名单名称") + private String whitelistName; + + /** 白名单类型 */ + @Excel(name = "白名单类型") + private Long whitelistType; + + /** 白名单模式 */ + @Excel(name = "白名单模式") + private Long whitelistMode; + + /** 状态 */ + @Excel(name = "状态") + private Long status; + + /** 备注 */ + @Excel(name = "备注") + private String remarks; + + /** 排序 */ + private Long orderNum; + + /** 逻辑删除,0:未删除,1:删除 */ + private Long isDelete; + + /** 创建人 */ + private String created; + + /** 更新人 */ + private String modified; + + /** 白名单成员列信息 */ + private List deploymentWhitelistListList; + + /** 开始时间 */ + private Date beginTime; + + /** 开始时间 */ + private Date endTime; + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("appId", getAppId()) + .append("appCode", getAppCode()) + .append("whitelistName", getWhitelistName()) + .append("whitelistType", getWhitelistType()) + .append("whitelistMode", getWhitelistMode()) + .append("status", getStatus()) + .append("remarks", getRemarks()) + .append("orderNum", getOrderNum()) + .append("isDelete", getIsDelete()) + .append("created", getCreated()) + .append("modified", getModified()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("deploymentWhitelistListList", getDeploymentWhitelistListList()) + .toString(); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentWhitelistList.java b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentWhitelistList.java new file mode 100644 index 0000000..a24aae5 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/DeploymentWhitelistList.java @@ -0,0 +1,165 @@ +package com.sf.system.deployment.domain; + +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; + +/** + * 白名单成员列对象 DEPLOYMENT_WHITELIST_LIST + * + * @author ztzh + * @date 2024-05-06 + */ +public class DeploymentWhitelistList extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 白名单id */ + @Excel(name = "白名单id") + private Long whitelistId; + + /** 应用id */ + @Excel(name = "应用id") + private Long appId; + + /** 应用code */ + @Excel(name = "应用code") + private String appCode; + + /** 人员或设备id */ + @Excel(name = "人员或设备id") + private String user; + + /** 备注 */ + @Excel(name = "备注") + private String remarks; + + /** 排序 */ + @Excel(name = "排序") + private Long orderNum; + + /** 逻辑删除,0:未删除,1:删除 */ + @Excel(name = "逻辑删除,0:未删除,1:删除") + private Long isDelete; + + /** 创建人 */ + @Excel(name = "创建人") + private String created; + + /** 更新人 */ + @Excel(name = "更新人") + private String modified; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setWhitelistId(Long whitelistId) + { + this.whitelistId = whitelistId; + } + + public Long getWhitelistId() + { + return whitelistId; + } + public void setAppId(Long appId) + { + this.appId = appId; + } + + public Long getAppId() + { + return appId; + } + public void setAppCode(String appCode) + { + this.appCode = appCode; + } + + public String getAppCode() + { + return appCode; + } + public void setUser(String user) + { + this.user = user; + } + + public String getUser() + { + return user; + } + public void setRemarks(String remarks) + { + this.remarks = remarks; + } + + public String getRemarks() + { + return remarks; + } + public void setOrderNum(Long orderNum) + { + this.orderNum = orderNum; + } + + public Long getOrderNum() + { + return orderNum; + } + public void setIsDelete(Long isDelete) + { + this.isDelete = isDelete; + } + + public Long getIsDelete() + { + return isDelete; + } + public void setCreated(String created) + { + this.created = created; + } + + public String getCreated() + { + return created; + } + public void setModified(String modified) + { + this.modified = modified; + } + + public String getModified() + { + return modified; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("whitelistId", getWhitelistId()) + .append("appId", getAppId()) + .append("appCode", getAppCode()) + .append("user", getUser()) + .append("remarks", getRemarks()) + .append("orderNum", getOrderNum()) + .append("isDelete", getIsDelete()) + .append("created", getCreated()) + .append("modified", getModified()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/SysApkInfo.java b/sf-system/src/main/java/com/sf/system/deployment/domain/SysApkInfo.java new file mode 100644 index 0000000..b08bd48 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/SysApkInfo.java @@ -0,0 +1,238 @@ +package com.sf.system.deployment.domain; + +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; + +/** + * 安装包管理(新)对象 SYS_APK_INFO + * + * @author ztzh + * @date 2024-04-11 + */ +public class SysApkInfo extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long id; + + /** + * 上传类型 + */ + private String appId; + + + private String appCode; + + /** + * 上传类型 + */ + @Excel(name = "上传类型") + private String uploadingType; + + /** + * 安装包名称 + */ + @Excel(name = "安装包名称") + private String sysApkName; + + /** + * 版本号 + */ + @Excel(name = "版本号") + private String version; + + /** + * 安装包 + */ + @Excel(name = "安装包") + private String sysApk; + + /** + * 安装包大小 + */ + @Excel(name = "安装包大小") + private String sysApkSize; + + /** + * 安装包类型 + */ + @Excel(name = "安装包类型") + private String sysType; + + /** + * 上传状态 + */ + private Long uploadingStatus; + + /** + * 日志id + */ + private Long uploadingLogId; + + /** + * 排序 + */ + private Long orderNum; + + /** + * 逻辑删除,0:未删除,1:删除 + */ + private Long isDelete; + + /** + * 创建人 + */ + @Excel(name = "创建人") + private String created; + + /** + * 更新人 + */ + private String modified; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setUploadingType(String uploadingType) { + this.uploadingType = uploadingType; + } + + public String getUploadingType() { + return uploadingType; + } + + public void setSysApkName(String sysApkName) { + this.sysApkName = sysApkName; + } + + public String getSysApkName() { + return sysApkName; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getVersion() { + return version; + } + + public void setSysApk(String sysApk) { + this.sysApk = sysApk; + } + + public String getSysApk() { + return sysApk; + } + + public void setSysApkSize(String sysApkSize) { + this.sysApkSize = sysApkSize; + } + + public String getSysApkSize() { + return sysApkSize; + } + + public void setSysType(String sysType) { + this.sysType = sysType; + } + + public String getSysType() { + return sysType; + } + + public void setUploadingStatus(Long uploadingStatus) { + this.uploadingStatus = uploadingStatus; + } + + public Long getUploadingStatus() { + return uploadingStatus; + } + + public void setUploadingLogId(Long uploadingLogId) { + this.uploadingLogId = uploadingLogId; + } + + public Long getUploadingLogId() { + return uploadingLogId; + } + + public void setOrderNum(Long orderNum) { + this.orderNum = orderNum; + } + + public Long getOrderNum() { + return orderNum; + } + + public void setIsDelete(Long isDelete) { + this.isDelete = isDelete; + } + + public Long getIsDelete() { + return isDelete; + } + + public void setCreated(String created) { + this.created = created; + } + + public String getCreated() { + return created; + } + + public void setModified(String modified) { + this.modified = modified; + } + + public String getModified() { + return modified; + } + + public String getAppCode() { + return appCode; + } + + public void setAppCode(String appCode) { + this.appCode = appCode; + } + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("uploadingType", getUploadingType()) + .append("sysApkName", getSysApkName()) + .append("version", getVersion()) + .append("sysApk", getSysApk()) + .append("sysApkSize", getSysApkSize()) + .append("sysType", getSysType()) + .append("uploadingStatus", getUploadingStatus()) + .append("uploadingLogId", getUploadingLogId()) + .append("orderNum", getOrderNum()) + .append("isDelete", getIsDelete()) + .append("created", getCreated()) + .append("modified", getModified()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("appCode", getAppId()) + .append("appId", getAppCode()) + .toString(); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/rqs/AddWhiteMemberRequest.java b/sf-system/src/main/java/com/sf/system/deployment/domain/rqs/AddWhiteMemberRequest.java new file mode 100755 index 0000000..05984bf --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/rqs/AddWhiteMemberRequest.java @@ -0,0 +1,16 @@ +package com.sf.system.deployment.domain.rqs; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class AddWhiteMemberRequest { + + @NotBlank + private Long whitelistId; + + @NotBlank + private String userId; +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/domain/rqs/ModuleListByCoreRequest.java b/sf-system/src/main/java/com/sf/system/deployment/domain/rqs/ModuleListByCoreRequest.java new file mode 100755 index 0000000..30adbdb --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/domain/rqs/ModuleListByCoreRequest.java @@ -0,0 +1,19 @@ +package com.sf.system.deployment.domain.rqs; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + + +@Data +public class ModuleListByCoreRequest { + + @NotBlank + private String appCore; + + @NotBlank + private String sysType; + + @NotBlank + private String moduleType; +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java index 195e6ca..905b7cc 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java +++ b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentApplyEnvironmentMapper.java @@ -1,4 +1,4 @@ -package com.sf.system.deployment.mapper; +package com.sf.deployment.mapper; import com.sf.system.deployment.domain.DeploymentApplyEnvironment; diff --git a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentModuleListMapper.java b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentModuleListMapper.java new file mode 100644 index 0000000..614b741 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentModuleListMapper.java @@ -0,0 +1,61 @@ +package com.sf.system.deployment.mapper; + +import java.util.List; +import com.sf.system.deployment.domain.DeploymentModuleList; + +/** + * H5模块包Mapper接口 + * + * @author ztzh + * @date 2024-04-22 + */ +public interface DeploymentModuleListMapper +{ + /** + * 查询H5模块包 + * + * @param id H5模块包主键 + * @return H5模块包 + */ + public DeploymentModuleList selectDeploymentModuleListById(Long id); + + /** + * 查询H5模块包列表 + * + * @param deploymentModuleList H5模块包 + * @return H5模块包集合 + */ + public List selectDeploymentModuleListList(DeploymentModuleList deploymentModuleList); + + /** + * 新增H5模块包 + * + * @param deploymentModuleList H5模块包 + * @return 结果 + */ + public int insertDeploymentModuleList(DeploymentModuleList deploymentModuleList); + + /** + * 修改H5模块包 + * + * @param deploymentModuleList H5模块包 + * @return 结果 + */ + public int updateDeploymentModuleList(DeploymentModuleList deploymentModuleList); + + /** + * 删除H5模块包 + * + * @param id H5模块包主键 + * @return 结果 + */ + public int deleteDeploymentModuleListById(Long id); + + /** + * 批量删除H5模块包 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDeploymentModuleListByIds(Long[] ids); +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentWhitelistInfoMapper.java b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentWhitelistInfoMapper.java new file mode 100644 index 0000000..1f0e884 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentWhitelistInfoMapper.java @@ -0,0 +1,89 @@ +package com.sf.system.deployment.mapper; + +import com.sf.system.deployment.domain.DeploymentWhitelistInfo; +import com.sf.system.deployment.domain.DeploymentWhitelistList; + +import java.util.List; + +/** + * 白名单列表Mapper接口 + * + * @author ztzh + * @date 2024-05-06 + */ +public interface DeploymentWhitelistInfoMapper +{ + /** + * 查询白名单列表 + * + * @param id 白名单列表主键 + * @return 白名单列表 + */ + public DeploymentWhitelistInfo selectDeploymentWhitelistInfoById(Long id); + + /** + * 查询白名单列表列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 白名单列表集合 + */ + public List selectDeploymentWhitelistInfoList(DeploymentWhitelistInfo deploymentWhitelistInfo); + + /** + * 新增白名单列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 结果 + */ + public int insertDeploymentWhitelistInfo(DeploymentWhitelistInfo deploymentWhitelistInfo); + + /** + * 修改白名单列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 结果 + */ + public int updateDeploymentWhitelistInfo(DeploymentWhitelistInfo deploymentWhitelistInfo); + + /** + * 删除白名单列表 + * + * @param id 白名单列表主键 + * @return 结果 + */ + public int deleteDeploymentWhitelistInfoById(Long id); + + + /** + * 批量删除白名单列表 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDeploymentWhitelistInfoByIds(Long[] ids); + + /** + * 批量删除白名单成员列 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDeploymentWhitelistListByWhitelistIds(Long[] ids); + + /** + * 批量新增白名单成员列 + * + * @param deploymentWhitelistListList 白名单成员列列表 + * @return 结果 + */ + public int batchDeploymentWhitelistList(List deploymentWhitelistListList); + + + /** + * 通过白名单列表主键删除白名单成员列信息 + * + * @param id 白名单列表ID + * @return 结果 + */ + public int deleteDeploymentWhitelistListByWhitelistId(Long id); +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentWhitelistListMapper.java b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentWhitelistListMapper.java new file mode 100644 index 0000000..d242af0 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/mapper/DeploymentWhitelistListMapper.java @@ -0,0 +1,62 @@ +package com.sf.system.deployment.mapper; + +import com.sf.system.deployment.domain.DeploymentWhitelistList; + +import java.util.List; + +/** + * 白名单成员列Mapper接口 + * + * @author ztzh + * @date 2024-05-06 + */ +public interface DeploymentWhitelistListMapper +{ + /** + * 查询白名单成员列 + * + * @param id 白名单成员列主键 + * @return 白名单成员列 + */ + public DeploymentWhitelistList selectDeploymentWhitelistListById(Long id); + + /** + * 查询白名单成员列列表 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 白名单成员列集合 + */ + public List selectDeploymentWhitelistListList(DeploymentWhitelistList deploymentWhitelistList); + + /** + * 新增白名单成员列 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 结果 + */ + public int insertDeploymentWhitelistList(DeploymentWhitelistList deploymentWhitelistList); + + /** + * 修改白名单成员列 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 结果 + */ + public int updateDeploymentWhitelistList(DeploymentWhitelistList deploymentWhitelistList); + + /** + * 删除白名单成员列 + * + * @param id 白名单成员列主键 + * @return 结果 + */ + public int deleteDeploymentWhitelistListById(Long id); + + /** + * 批量删除白名单成员列 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDeploymentWhitelistListByIds(Long[] ids); +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/mapper/SysApkInfoMapper.java b/sf-system/src/main/java/com/sf/system/deployment/mapper/SysApkInfoMapper.java new file mode 100644 index 0000000..71ce17f --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/mapper/SysApkInfoMapper.java @@ -0,0 +1,63 @@ +package com.sf.system.deployment.mapper; + + +import com.sf.system.deployment.domain.SysApkInfo; + +import java.util.List; + +/** + * 安装包管理(新)Mapper接口 + * + * @author ztzh + * @date 2024-04-11 + */ +public interface SysApkInfoMapper +{ + /** + * 查询安装包管理(新) + * + * @param id 安装包管理(新)主键 + * @return 安装包管理(新) + */ + public SysApkInfo selectSysApkInfoById(Long id); + + /** + * 查询安装包管理(新)列表 + * + * @param sysApkInfo 安装包管理(新) + * @return 安装包管理(新)集合 + */ + public List selectSysApkInfoList(SysApkInfo sysApkInfo); + + /** + * 新增安装包管理(新) + * + * @param sysApkInfo 安装包管理(新) + * @return 结果 + */ + public int insertSysApkInfo(SysApkInfo sysApkInfo); + + /** + * 修改安装包管理(新) + * + * @param sysApkInfo 安装包管理(新) + * @return 结果 + */ + public int updateSysApkInfo(SysApkInfo sysApkInfo); + + /** + * 删除安装包管理(新) + * + * @param id 安装包管理(新)主键 + * @return 结果 + */ + public int deleteSysApkInfoById(Long id); + + /** + * 批量删除安装包管理(新) + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysApkInfoByIds(Long[] ids); +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java index bac2f75..3f8d855 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java +++ b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentApplyEnvironmentService.java @@ -1,4 +1,4 @@ -package com.sf.system.deployment.service; +package com.sf.deployment.service; import com.sf.system.deployment.domain.DeploymentApplyEnvironment; diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentModuleListService.java b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentModuleListService.java new file mode 100644 index 0000000..3e503b1 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentModuleListService.java @@ -0,0 +1,61 @@ +package com.sf.system.deployment.service; + +import java.util.List; +import com.sf.system.deployment.domain.DeploymentModuleList; + +/** + * H5模块包Service接口 + * + * @author ztzh + * @date 2024-04-22 + */ +public interface IDeploymentModuleListService +{ + /** + * 查询H5模块包 + * + * @param id H5模块包主键 + * @return H5模块包 + */ + public DeploymentModuleList selectDeploymentModuleListById(Long id); + + /** + * 查询H5模块包列表 + * + * @param deploymentModuleList H5模块包 + * @return H5模块包集合 + */ + public List selectDeploymentModuleListList(DeploymentModuleList deploymentModuleList); + + /** + * 新增H5模块包 + * + * @param deploymentModuleList H5模块包 + * @return 结果 + */ + public int insertDeploymentModuleList(DeploymentModuleList deploymentModuleList); + + /** + * 修改H5模块包 + * + * @param deploymentModuleList H5模块包 + * @return 结果 + */ + public int updateDeploymentModuleList(DeploymentModuleList deploymentModuleList); + + /** + * 批量删除H5模块包 + * + * @param ids 需要删除的H5模块包主键集合 + * @return 结果 + */ + public int deleteDeploymentModuleListByIds(Long[] ids); + + /** + * 删除H5模块包信息 + * + * @param id H5模块包主键 + * @return 结果 + */ + public int deleteDeploymentModuleListById(Long id); +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentServicePublishService.java b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentServicePublishService.java index a9ab32a..e307482 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentServicePublishService.java +++ b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentServicePublishService.java @@ -2,6 +2,7 @@ package com.sf.system.deployment.service; import com.sf.system.deployment.domain.DeploymentServicePublish; +import java.io.IOException; import java.util.List; /** @@ -59,4 +60,8 @@ public interface IDeploymentServicePublishService * @return 结果 */ public int deleteDeploymentServicePublishById(Long id); + + public int publish(DeploymentServicePublish deploymentServicePublish) throws Exception; + + public int soldOut(DeploymentServicePublish deploymentServicePublish); } diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentWhitelistInfoService.java b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentWhitelistInfoService.java new file mode 100644 index 0000000..5f337e7 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentWhitelistInfoService.java @@ -0,0 +1,77 @@ +package com.sf.system.deployment.service; + +import com.sf.system.deployment.domain.DeploymentWhitelistInfo; + +import java.util.List; + +/** + * 白名单列表Service接口 + * + * @author ztzh + * @date 2024-05-06 + */ +public interface IDeploymentWhitelistInfoService +{ + /** + * 查询白名单列表 + * + * @param id 白名单列表主键 + * @return 白名单列表 + */ + public DeploymentWhitelistInfo selectDeploymentWhitelistInfoById(Long id); + + /** + * 查询白名单列表列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 白名单列表集合 + */ + public List selectDeploymentWhitelistInfoList(DeploymentWhitelistInfo deploymentWhitelistInfo); + + /** + * 新增白名单列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 结果 + */ + public int insertDeploymentWhitelistInfo(DeploymentWhitelistInfo deploymentWhitelistInfo); + + + /** + * 修改白名单列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 结果 + */ + public int updateDeploymentWhitelistInfo(DeploymentWhitelistInfo deploymentWhitelistInfo); + + /** + * 发布功能 + * @param id + * @return + */ + public int publish(Long id); + /** + * 下架功能 + * @param id + * @return + */ + public int soldOut(Long id); + + /** + * 批量删除白名单列表 + * + * @param ids 需要删除的白名单列表主键集合 + * @return 结果 + */ + public int deleteDeploymentWhitelistInfoByIds(Long[] ids); + + /** + * 删除白名单列表信息 + * + * @param id 白名单列表主键 + * @return 结果 + */ + public int deleteDeploymentWhitelistInfoById(Long id); + +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentWhitelistListService.java b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentWhitelistListService.java new file mode 100644 index 0000000..b6bd394 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/IDeploymentWhitelistListService.java @@ -0,0 +1,67 @@ +package com.sf.system.deployment.service; + +import com.sf.system.deployment.domain.DeploymentWhitelistList; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +/** + * 白名单成员列Service接口 + * + * @author ztzh + * @date 2024-05-06 + */ +public interface IDeploymentWhitelistListService +{ + /** + * 查询白名单成员列 + * + * @param id 白名单成员列主键 + * @return 白名单成员列 + */ + public DeploymentWhitelistList selectDeploymentWhitelistListById(Long id); + + /** + * 查询白名单成员列列表 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 白名单成员列集合 + */ + public List selectDeploymentWhitelistListList(DeploymentWhitelistList deploymentWhitelistList); + + /** + * 新增白名单成员列 + * + * @param deploymentWhitelistList 白名单成员列 + * @param username + * @return 结果 + */ + public int insertDeploymentWhitelistList(DeploymentWhitelistList deploymentWhitelistList, String username); + + /** + * 修改白名单成员列 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 结果 + */ + public int updateDeploymentWhitelistList(DeploymentWhitelistList deploymentWhitelistList); + + /** + * 批量删除白名单成员列 + * + * @param ids 需要删除的白名单成员列主键集合 + * @return 结果 + */ + public int deleteDeploymentWhitelistListByIds(Long[] ids); + + /** + * 删除白名单成员列信息 + * + * @param id 白名单成员列主键 + * @return 结果 + */ + public int deleteDeploymentWhitelistListById(Long id); + + public int batchImport(MultipartFile file, Long whitelistId, String username) throws IOException; +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/ISysApkInfoService.java b/sf-system/src/main/java/com/sf/system/deployment/service/ISysApkInfoService.java new file mode 100644 index 0000000..1925ff2 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/ISysApkInfoService.java @@ -0,0 +1,63 @@ +package com.sf.system.deployment.service; + + +import com.sf.system.deployment.domain.SysApkInfo; + +import java.util.List; + +/** + * 安装包管理(新)Service接口 + * + * @author ztzh + * @date 2024-04-11 + */ +public interface ISysApkInfoService +{ + /** + * 查询安装包管理(新) + * + * @param id 安装包管理(新)主键 + * @return 安装包管理(新) + */ + public SysApkInfo selectSysApkInfoById(Long id); + + /** + * 查询安装包管理(新)列表 + * + * @param sysApkInfo 安装包管理(新) + * @return 安装包管理(新)集合 + */ + public List selectSysApkInfoList(SysApkInfo sysApkInfo); + + /** + * 新增安装包管理(新) + * + * @param sysApkInfo 安装包管理(新) + * @return 结果 + */ + public int insertSysApkInfo(SysApkInfo sysApkInfo); + + /** + * 修改安装包管理(新) + * + * @param sysApkInfo 安装包管理(新) + * @return 结果 + */ + public int updateSysApkInfo(SysApkInfo sysApkInfo); + + /** + * 批量删除安装包管理(新) + * + * @param ids 需要删除的安装包管理(新)主键集合 + * @return 结果 + */ + public int deleteSysApkInfoByIds(Long[] ids); + + /** + * 删除安装包管理(新)信息 + * + * @param id 安装包管理(新)主键 + * @return 结果 + */ + public int deleteSysApkInfoById(Long id); +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java index d988e0b..47945cc 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentApplyEnvironmentServiceImpl.java @@ -1,14 +1,12 @@ -package com.sf.system.deployment.service.impl; +package com.sf.deployment.service.impl; import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - import com.sf.common.utils.DateUtils; import com.sf.system.deployment.domain.DeploymentApplyEnvironment; -import com.sf.system.deployment.mapper.DeploymentApplyEnvironmentMapper; -import com.sf.system.deployment.service.IDeploymentApplyEnvironmentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.sf.deployment.mapper.DeploymentApplyEnvironmentMapper; +import com.sf.deployment.service.IDeploymentApplyEnvironmentService; /** * 环境维护Service业务层处理 diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentModuleListServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentModuleListServiceImpl.java new file mode 100644 index 0000000..12de1f6 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentModuleListServiceImpl.java @@ -0,0 +1,96 @@ +package com.sf.system.deployment.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.deployment.mapper.DeploymentModuleListMapper; +import com.sf.system.deployment.domain.DeploymentModuleList; +import com.sf.system.deployment.service.IDeploymentModuleListService; + +/** + * H5模块包Service业务层处理 + * + * @author ztzh + * @date 2024-04-22 + */ +@Service +public class DeploymentModuleListServiceImpl implements IDeploymentModuleListService +{ + @Autowired + private DeploymentModuleListMapper deploymentModuleListMapper; + + /** + * 查询H5模块包 + * + * @param id H5模块包主键 + * @return H5模块包 + */ + @Override + public DeploymentModuleList selectDeploymentModuleListById(Long id) + { + return deploymentModuleListMapper.selectDeploymentModuleListById(id); + } + + /** + * 查询H5模块包列表 + * + * @param deploymentModuleList H5模块包 + * @return H5模块包 + */ + @Override + public List selectDeploymentModuleListList(DeploymentModuleList deploymentModuleList) + { + return deploymentModuleListMapper.selectDeploymentModuleListList(deploymentModuleList); + } + + /** + * 新增H5模块包 + * + * @param deploymentModuleList H5模块包 + * @return 结果 + */ + @Override + public int insertDeploymentModuleList(DeploymentModuleList deploymentModuleList) + { + deploymentModuleList.setCreateTime(DateUtils.getNowDate()); + return deploymentModuleListMapper.insertDeploymentModuleList(deploymentModuleList); + } + + /** + * 修改H5模块包 + * + * @param deploymentModuleList H5模块包 + * @return 结果 + */ + @Override + public int updateDeploymentModuleList(DeploymentModuleList deploymentModuleList) + { + deploymentModuleList.setUpdateTime(DateUtils.getNowDate()); + return deploymentModuleListMapper.updateDeploymentModuleList(deploymentModuleList); + } + + /** + * 批量删除H5模块包 + * + * @param ids 需要删除的H5模块包主键 + * @return 结果 + */ + @Override + public int deleteDeploymentModuleListByIds(Long[] ids) + { + return deploymentModuleListMapper.deleteDeploymentModuleListByIds(ids); + } + + /** + * 删除H5模块包信息 + * + * @param id H5模块包主键 + * @return 结果 + */ + @Override + public int deleteDeploymentModuleListById(Long id) + { + return deploymentModuleListMapper.deleteDeploymentModuleListById(id); + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentServicePublishServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentServicePublishServiceImpl.java index 1e1749c..bde7b26 100644 --- a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentServicePublishServiceImpl.java +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentServicePublishServiceImpl.java @@ -1,12 +1,24 @@ package com.sf.system.deployment.service.impl; +import java.io.IOException; import java.util.List; + +import com.sf.common.constant.HttpStatus; +import com.sf.common.exception.ServiceException; import com.sf.common.utils.DateUtils; +import com.sf.common.utils.file.QRCodeUtils; +import com.sf.file.domain.SysOss; +import com.sf.file.service.ISysOssService; import com.sf.system.deployment.domain.DeploymentServicePublish; +import com.sf.system.deployment.domain.SysApkInfo; import com.sf.system.deployment.mapper.DeploymentServicePublishMapper; import com.sf.system.deployment.service.IDeploymentServicePublishService; +import com.sf.system.deployment.service.ISysApkInfoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; /** * 发布列表Service业务层处理 @@ -17,6 +29,10 @@ import org.springframework.stereotype.Service; @Service public class DeploymentServicePublishServiceImpl implements IDeploymentServicePublishService { + @Resource + private ISysApkInfoService iSysApkInfoService; + @Resource + private ISysOssService iSysOssService; @Autowired private DeploymentServicePublishMapper deploymentServicePublishMapper; @@ -93,4 +109,26 @@ public class DeploymentServicePublishServiceImpl implements IDeploymentServicePu { return deploymentServicePublishMapper.deleteDeploymentServicePublishById(id); } + + @Override + public int publish(DeploymentServicePublish deploymentServicePublish) throws Exception { + if(1 == deploymentServicePublish.getStatus()){ + throw new ServiceException("安装包已发布", HttpStatus.WARN); + } + deploymentServicePublish.setUpdateTime(DateUtils.getNowDate()); + SysApkInfo sysApkInfo = iSysApkInfoService.selectSysApkInfoById(deploymentServicePublish.getApkId()); + SysOss oss = iSysOssService.upload(QRCodeUtils.generateQRCode(sysApkInfo.getSysApk())); + deploymentServicePublish.setStatus(1L); + deploymentServicePublish.setQuickMark(oss.getUrl()); + return deploymentServicePublishMapper.updateDeploymentServicePublish(deploymentServicePublish); + } + + @Override + public int soldOut(DeploymentServicePublish deploymentServicePublish) { + if(2 == deploymentServicePublish.getStatus()){ + throw new ServiceException("安装包已下架", HttpStatus.WARN); + } + deploymentServicePublish.setStatus(2L); + return deploymentServicePublishMapper.updateDeploymentServicePublish(deploymentServicePublish); + } } diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentWhitelistInfoServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentWhitelistInfoServiceImpl.java new file mode 100644 index 0000000..c302145 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentWhitelistInfoServiceImpl.java @@ -0,0 +1,149 @@ +package com.sf.system.deployment.service.impl; + +import java.util.List; + +import com.sf.common.utils.DateUtils; +import com.sf.system.deployment.domain.DeploymentWhitelistInfo; +import com.sf.system.deployment.domain.DeploymentWhitelistList; +import com.sf.system.deployment.mapper.DeploymentWhitelistInfoMapper; +import com.sf.system.deployment.mapper.DeploymentWhitelistListMapper; +import com.sf.system.deployment.service.IDeploymentWhitelistInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; + +import com.sf.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; + +/** + * 白名单列表Service业务层处理 + * + * @author ztzh + * @date 2024-05-06 + */ +@Service +public class DeploymentWhitelistInfoServiceImpl implements IDeploymentWhitelistInfoService { + @Autowired + private DeploymentWhitelistInfoMapper deploymentWhitelistInfoMapper; + @Autowired + private DeploymentWhitelistListMapper deploymentWhitelistListMapper; + + /** + * 查询白名单列表 + * + * @param id 白名单列表主键 + * @return 白名单列表 + */ + @Override + public DeploymentWhitelistInfo selectDeploymentWhitelistInfoById(Long id) { + return deploymentWhitelistInfoMapper.selectDeploymentWhitelistInfoById(id); + } + + /** + * 查询白名单列表列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 白名单列表 + */ + @Override + public List selectDeploymentWhitelistInfoList(DeploymentWhitelistInfo deploymentWhitelistInfo) { + List infos = deploymentWhitelistInfoMapper.selectDeploymentWhitelistInfoList(deploymentWhitelistInfo); + infos.forEach(deploymentWhitelistInfo1 -> { + DeploymentWhitelistList deploymentWhitelistList = new DeploymentWhitelistList(); + deploymentWhitelistList.setWhitelistId(deploymentWhitelistInfo1.getId()); + List list = deploymentWhitelistListMapper.selectDeploymentWhitelistListList(deploymentWhitelistList); + deploymentWhitelistInfo1.setDeploymentWhitelistListList(list); + }); + return infos; + } + + /** + * 新增白名单列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 结果 + */ + @Transactional + @Override + public int insertDeploymentWhitelistInfo(DeploymentWhitelistInfo deploymentWhitelistInfo) { + deploymentWhitelistInfo.setCreateTime(DateUtils.getNowDate()); + int rows = deploymentWhitelistInfoMapper.insertDeploymentWhitelistInfo(deploymentWhitelistInfo); + insertDeploymentWhitelistList(deploymentWhitelistInfo); + return rows; + } + + + /** + * 修改白名单列表 + * + * @param deploymentWhitelistInfo 白名单列表 + * @return 结果 + */ + @Transactional + @Override + public int updateDeploymentWhitelistInfo(DeploymentWhitelistInfo deploymentWhitelistInfo) { + return deploymentWhitelistInfoMapper.updateDeploymentWhitelistInfo(deploymentWhitelistInfo); + } + + @Override + public int publish(Long id) { + DeploymentWhitelistInfo deploymentWhitelistInfo = deploymentWhitelistInfoMapper.selectDeploymentWhitelistInfoById(id); + deploymentWhitelistInfo.setStatus(1L); + return deploymentWhitelistInfoMapper.updateDeploymentWhitelistInfo(deploymentWhitelistInfo); + } + + @Override + public int soldOut(Long id) { + DeploymentWhitelistInfo deploymentWhitelistInfo = deploymentWhitelistInfoMapper.selectDeploymentWhitelistInfoById(id); + deploymentWhitelistInfo.setStatus(0L); + return deploymentWhitelistInfoMapper.updateDeploymentWhitelistInfo(deploymentWhitelistInfo); + } + + /** + * 批量删除白名单列表 + * + * @param ids 需要删除的白名单列表主键 + * @return 结果 + */ + @Transactional + @Override + public int deleteDeploymentWhitelistInfoByIds(Long[] ids) { + deploymentWhitelistInfoMapper.deleteDeploymentWhitelistListByWhitelistIds(ids); + return deploymentWhitelistInfoMapper.deleteDeploymentWhitelistInfoByIds(ids); + } + + /** + * 删除白名单列表信息 + * + * @param id 白名单列表主键 + * @return 结果 + */ + @Transactional + @Override + public int deleteDeploymentWhitelistInfoById(Long id) { + deploymentWhitelistInfoMapper.deleteDeploymentWhitelistListByWhitelistId(id); + return deploymentWhitelistInfoMapper.deleteDeploymentWhitelistInfoById(id); + } + + + /** + * 新增白名单成员列信息 + * + * @param deploymentWhitelistInfo 白名单列表对象 + */ + public void insertDeploymentWhitelistList(DeploymentWhitelistInfo deploymentWhitelistInfo) { + List deploymentWhitelistListList = deploymentWhitelistInfo.getDeploymentWhitelistListList(); + Long id = deploymentWhitelistInfo.getId(); + if (StringUtils.isNotNull(deploymentWhitelistListList)) { + List list = new ArrayList(); + for (DeploymentWhitelistList deploymentWhitelistList : deploymentWhitelistListList) { + deploymentWhitelistList.setWhitelistId(id); + list.add(deploymentWhitelistList); + } + if (list.size() > 0) { + deploymentWhitelistInfoMapper.batchDeploymentWhitelistList(list); + } + } + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentWhitelistListServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentWhitelistListServiceImpl.java new file mode 100644 index 0000000..812b7ad --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/DeploymentWhitelistListServiceImpl.java @@ -0,0 +1,132 @@ +package com.sf.system.deployment.service.impl; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import com.sf.common.enums.RequestHeaderEnums; +import com.sf.common.utils.DateUtils; +import com.sf.common.utils.StringUtils; +import com.sf.common.utils.http.RequestUtils; +import com.sf.system.deployment.domain.DeploymentWhitelistList; +import com.sf.system.deployment.mapper.DeploymentWhitelistListMapper; +import com.sf.system.deployment.service.IDeploymentWhitelistListService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +/** + * 白名单成员列Service业务层处理 + * + * @author ztzh + * @date 2024-05-06 + */ +@Service +public class DeploymentWhitelistListServiceImpl implements IDeploymentWhitelistListService +{ + @Autowired + private DeploymentWhitelistListMapper deploymentWhitelistListMapper; + + /** + * 查询白名单成员列 + * + * @param id 白名单成员列主键 + * @return 白名单成员列 + */ + @Override + public DeploymentWhitelistList selectDeploymentWhitelistListById(Long id) + { + return deploymentWhitelistListMapper.selectDeploymentWhitelistListById(id); + } + + /** + * 查询白名单成员列列表 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 白名单成员列 + */ + @Override + public List selectDeploymentWhitelistListList(DeploymentWhitelistList deploymentWhitelistList) + { + return deploymentWhitelistListMapper.selectDeploymentWhitelistListList(deploymentWhitelistList); + } + + /** + * 新增白名单成员列 + * + * @param member 白名单成员列 + * @param username + * @return 结果 + */ + @Override + public int insertDeploymentWhitelistList(DeploymentWhitelistList member, String username) + { + if(null == member.getWhitelistId()){ + throw new SecurityException("参数异常WhitelistId为空"); + } + member.setAppCode(RequestUtils.getHeader(RequestHeaderEnums.APP_CODE.getCode())); + member.setCreated(username); + member.setCreateTime(DateUtils.getNowDate()); + member.setModified(username); + member.setUpdateTime(DateUtils.getNowDate()); + member.setCreateTime(DateUtils.getNowDate()); + return deploymentWhitelistListMapper.insertDeploymentWhitelistList(member); + } + + /** + * 修改白名单成员列 + * + * @param deploymentWhitelistList 白名单成员列 + * @return 结果 + */ + @Override + public int updateDeploymentWhitelistList(DeploymentWhitelistList deploymentWhitelistList) + { + deploymentWhitelistList.setUpdateTime(DateUtils.getNowDate()); + return deploymentWhitelistListMapper.updateDeploymentWhitelistList(deploymentWhitelistList); + } + + /** + * 批量删除白名单成员列 + * + * @param ids 需要删除的白名单成员列主键 + * @return 结果 + */ + @Override + public int deleteDeploymentWhitelistListByIds(Long[] ids) + { + return deploymentWhitelistListMapper.deleteDeploymentWhitelistListByIds(ids); + } + + /** + * 删除白名单成员列信息 + * + * @param id 白名单成员列主键 + * @return 结果 + */ + @Override + public int deleteDeploymentWhitelistListById(Long id) + { + return deploymentWhitelistListMapper.deleteDeploymentWhitelistListById(id); + } + + @Override + public int batchImport(MultipartFile file, Long whitelistId, String username) throws IOException { + String content = ""; + try { + byte[] data = file.getBytes(); + content = new String(data, StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + } + content.replaceAll("/n",""); + String[] split = StringUtils.split(content, ","); + for (String user : split) { + DeploymentWhitelistList menber = new DeploymentWhitelistList(); + menber.setWhitelistId(whitelistId); + menber.setUser(user.trim()); + insertDeploymentWhitelistList(menber, username); + } + return 1; + } +} diff --git a/sf-system/src/main/java/com/sf/system/deployment/service/impl/SysApkInfoServiceImpl.java b/sf-system/src/main/java/com/sf/system/deployment/service/impl/SysApkInfoServiceImpl.java new file mode 100644 index 0000000..b0513ec --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/deployment/service/impl/SysApkInfoServiceImpl.java @@ -0,0 +1,98 @@ +package com.sf.system.deployment.service.impl; + +import com.sf.common.utils.DateUtils; +import com.sf.system.deployment.domain.SysApkInfo; +import com.sf.system.deployment.mapper.SysApkInfoMapper; +import com.sf.system.deployment.service.ISysApkInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 安装包管理(新)Service业务层处理 + * + * @author ztzh + * @date 2024-04-11 + */ +@Service +public class SysApkInfoServiceImpl implements ISysApkInfoService +{ + @Autowired + private SysApkInfoMapper sysApkInfoMapper; + + /** + * 查询安装包管理(新) + * + * @param id 安装包管理(新)主键 + * @return 安装包管理(新) + */ + @Override + public SysApkInfo selectSysApkInfoById(Long id) + { + return sysApkInfoMapper.selectSysApkInfoById(id); + } + + /** + * 查询安装包管理(新)列表 + * + * @param sysApkInfo 安装包管理(新) + * @return 安装包管理(新) + */ + @Override + public List selectSysApkInfoList(SysApkInfo sysApkInfo) + { + return sysApkInfoMapper.selectSysApkInfoList(sysApkInfo); + } + + /** + * 新增安装包管理(新) + * + * @param sysApkInfo 安装包管理(新) + * @return 结果 + */ + @Override + public int insertSysApkInfo(SysApkInfo sysApkInfo) + { + sysApkInfo.setCreateTime(DateUtils.getNowDate()); + sysApkInfo.setUpdateTime(DateUtils.getNowDate()); + return sysApkInfoMapper.insertSysApkInfo(sysApkInfo); + } + + /** + * 修改安装包管理(新) + * + * @param sysApkInfo 安装包管理(新) + * @return 结果 + */ + @Override + public int updateSysApkInfo(SysApkInfo sysApkInfo) + { + sysApkInfo.setUpdateTime(DateUtils.getNowDate()); + return sysApkInfoMapper.updateSysApkInfo(sysApkInfo); + } + + /** + * 批量删除安装包管理(新) + * + * @param ids 需要删除的安装包管理(新)主键 + * @return 结果 + */ + @Override + public int deleteSysApkInfoByIds(Long[] ids) + { + return sysApkInfoMapper.deleteSysApkInfoByIds(ids); + } + + /** + * 删除安装包管理(新)信息 + * + * @param id 安装包管理(新)主键 + * @return 结果 + */ + @Override + public int deleteSysApkInfoById(Long id) + { + return sysApkInfoMapper.deleteSysApkInfoById(id); + } +} diff --git a/sf-system/src/main/java/com/sf/system/domain/UserMember.java b/sf-system/src/main/java/com/sf/system/domain/UserMember.java new file mode 100644 index 0000000..fa41ff1 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/domain/UserMember.java @@ -0,0 +1,117 @@ +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:生效状态 + 2:已到期 + 3:尝试扣费 + 5:撤销 + 6:暂停 */ + @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(); + } +} diff --git a/sf-system/src/main/java/com/sf/system/mapper/UserMemberMapper.java b/sf-system/src/main/java/com/sf/system/mapper/UserMemberMapper.java new file mode 100644 index 0000000..f5ad663 --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/mapper/UserMemberMapper.java @@ -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 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); +} diff --git a/sf-system/src/main/java/com/sf/system/service/IUserMemberService.java b/sf-system/src/main/java/com/sf/system/service/IUserMemberService.java new file mode 100644 index 0000000..8e40f1a --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/service/IUserMemberService.java @@ -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 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); +} diff --git a/sf-system/src/main/java/com/sf/system/service/impl/UserMemberServiceImpl.java b/sf-system/src/main/java/com/sf/system/service/impl/UserMemberServiceImpl.java new file mode 100644 index 0000000..6c2890c --- /dev/null +++ b/sf-system/src/main/java/com/sf/system/service/impl/UserMemberServiceImpl.java @@ -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 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); + } +} diff --git a/sf-system/src/main/resources/mapper/system/UserMemberMapper.xml b/sf-system/src/main/resources/mapper/system/UserMemberMapper.xml new file mode 100644 index 0000000..3c8a8a0 --- /dev/null +++ b/sf-system/src/main/resources/mapper/system/UserMemberMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + select id, member_level, subscription_status, user_id, create_time, update_time, integration, expiration_time from User_member + + + + + + + + + + insert into User_member + + member_level, + subscription_status, + user_id, + create_time, + update_time, + integration, + expiration_time, + + + #{memberLevel}, + #{subscriptionStatus}, + #{userId}, + #{createTime}, + #{updateTime}, + #{integration}, + #{expirationTime}, + + + + + update User_member + + member_level = #{memberLevel}, + subscription_status = #{subscriptionStatus}, + user_id = #{userId}, + create_time = #{createTime}, + update_time = #{updateTime}, + integration = #{integration}, + expiration_time = #{expirationTime}, + + where id = #{id} + + + + delete from User_member where id = #{id} + + + + delete from User_member where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-system/src/main/resources/mapper/system/deployment/DeploymentApplyEnvironmentMapper.xml b/sf-system/src/main/resources/mapper/system/deployment/DeploymentApplyEnvironmentMapper.xml index 81b5f73..0438c2c 100644 --- a/sf-system/src/main/resources/mapper/system/deployment/DeploymentApplyEnvironmentMapper.xml +++ b/sf-system/src/main/resources/mapper/system/deployment/DeploymentApplyEnvironmentMapper.xml @@ -7,7 +7,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - + @@ -20,12 +20,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, app_id, apply_code, name, server_address, remark, is_delete, create_dept, create_time, create_by, update_time, update_by from DEPLOYMENT_APPLY_ENVIRONMENT + select id, app_id, app_code, name, server_address, remark, is_delete, create_dept, create_time, create_by, update_time, update_by from DEPLOYMENT_APPLY_ENVIRONMENT @@ -39,7 +40,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" insert into DEPLOYMENT_APPLY_ENVIRONMENT app_id, - apply_code, + app_code, name, server_address, remark, @@ -52,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{appId}, - #{applyCode}, + #{appCode}, #{name}, #{serverAddress}, #{remark}, @@ -69,7 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update DEPLOYMENT_APPLY_ENVIRONMENT app_id = #{appId}, - apply_code = #{applyCode}, + apply_code = #{appCode}, name = #{name}, server_address = #{serverAddress}, remark = #{remark}, diff --git a/sf-system/src/main/resources/mapper/system/deployment/DeploymentModuleListMapper.xml b/sf-system/src/main/resources/mapper/system/deployment/DeploymentModuleListMapper.xml new file mode 100644 index 0000000..e8cc2f3 --- /dev/null +++ b/sf-system/src/main/resources/mapper/system/deployment/DeploymentModuleListMapper.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, app_id, app_code, uploading_type, module_name, version, module_url, module_size, sys_type, module_type, uploading_status, uploading_log_id, order_num, is_delete, created, modified, create_time, update_time from DEPLOYMENT_MODULE_LIST + + + + + + + + insert into DEPLOYMENT_MODULE_LIST + + app_id, + app_code, + uploading_type, + module_name, + version, + module_url, + module_size, + sys_type, + module_type, + uploading_status, + uploading_log_id, + order_num, + is_delete, + created, + modified, + create_time, + update_time, + + + #{appId}, + #{appCode}, + #{uploadingType}, + #{moduleName}, + #{version}, + #{moduleUrl}, + #{moduleSize}, + #{sysType}, + #{moduleType}, + #{uploadingStatus}, + #{uploadingLogId}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + + + + + update DEPLOYMENT_MODULE_LIST + + app_id = #{appId}, + app_code = #{appCode}, + uploading_type = #{uploadingType}, + module_name = #{moduleName}, + version = #{version}, + module_url = #{moduleUrl}, + module_size = #{moduleSize}, + sys_type = #{sysType}, + module_type = #{moduleType}, + uploading_status = #{uploadingStatus}, + uploading_log_id = #{uploadingLogId}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete + from DEPLOYMENT_MODULE_LIST + where id = #{id} + + + + delete from DEPLOYMENT_MODULE_LIST where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-system/src/main/resources/mapper/system/deployment/DeploymentServicePublishMapper.xml b/sf-system/src/main/resources/mapper/system/deployment/DeploymentServicePublishMapper.xml index bd838e4..c2623bb 100644 --- a/sf-system/src/main/resources/mapper/system/deployment/DeploymentServicePublishMapper.xml +++ b/sf-system/src/main/resources/mapper/system/deployment/DeploymentServicePublishMapper.xml @@ -8,6 +8,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + + @@ -32,13 +35,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, apply_id, app_name, system_type, apk_id, app_store_address, version, version_desc, publish_environment, publish_strategy, result_notify, public_version, force_update, download_verification, update_object, validity_start_time, validity_end_time, remark, status, is_delete, created, modified, create_time, update_time from DEPLOYMENT_SERVICE_PUBLISH + select id, apply_id, app_name, app_code,apk_url , quick_mark, system_type, apk_id, app_store_address, version, version_desc, publish_environment, publish_strategy, result_notify, public_version, force_update, download_verification, update_object, validity_start_time, validity_end_time, remark, status, is_delete, created, modified, create_time, update_time from DEPLOYMENT_SERVICE_PUBLISH + + + and app_code = #{appCode} + and whitelist_name like concat('%', #{whitelistName}, '%') + and whitelist_type = #{whitelistType} + and whitelist_mode = #{whitelistMode} + and status = #{status} + and remarks = #{remarks} + and create_time ]]> #{beginTime} + and create_time #{endTime} + + + + + + + insert into DEPLOYMENT_WHITELIST_INFO + + app_id, + app_code, + whitelist_name, + whitelist_type, + whitelist_mode, + status, + remarks, + order_num, + is_delete, + created, + modified, + create_time, + update_time, + + + #{appId}, + #{appCode}, + #{whitelistName}, + #{whitelistType}, + #{whitelistMode}, + #{status}, + #{remarks}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + + + + + + update DEPLOYMENT_WHITELIST_INFO + + app_id = #{appId}, + app_code = #{appCode}, + whitelist_name = #{whitelistName}, + whitelist_type = #{whitelistType}, + whitelist_mode = #{whitelistMode}, + status = #{status}, + remarks = #{remarks}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from DEPLOYMENT_WHITELIST_INFO where id = #{id} + + + + delete from DEPLOYMENT_WHITELIST_INFO where id in + + #{id} + + + + + delete from DEPLOYMENT_WHITELIST_LIST where whitelist_id in + + #{whitelistId} + + + + + delete from DEPLOYMENT_WHITELIST_LIST where whitelist_id = #{whitelistId} + + + + insert into DEPLOYMENT_WHITELIST_LIST( id, whitelist_id, app_id, app_code, user, remarks, order_num, is_delete, created, modified, create_time, update_time) values + + ( #{item.id}, #{item.whitelistId}, #{item.appId}, #{item.appCode}, #{item.user}, #{item.remarks}, #{item.orderNum}, #{item.isDelete}, #{item.created}, #{item.modified}, #{item.createTime}, #{item.updateTime}) + + + \ No newline at end of file diff --git a/sf-system/src/main/resources/mapper/system/deployment/DeploymentWhitelistListMapper.xml b/sf-system/src/main/resources/mapper/system/deployment/DeploymentWhitelistListMapper.xml new file mode 100644 index 0000000..63868a8 --- /dev/null +++ b/sf-system/src/main/resources/mapper/system/deployment/DeploymentWhitelistListMapper.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + select id, whitelist_id, app_id, app_code, user, remarks, order_num, is_delete, created, modified, create_time, update_time from DEPLOYMENT_WHITELIST_LIST + + + + + + + + insert into DEPLOYMENT_WHITELIST_LIST + + whitelist_id, + app_id, + app_code, + user, + remarks, + order_num, + is_delete, + created, + modified, + create_time, + update_time, + + + #{whitelistId}, + #{appId}, + #{appCode}, + #{user}, + #{remarks}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + + + + + update DEPLOYMENT_WHITELIST_LIST + + whitelist_id = #{whitelistId}, + app_id = #{appId}, + app_code = #{appCode}, + user = #{user}, + remarks = #{remarks}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from DEPLOYMENT_WHITELIST_LIST where id = #{id} + + + + delete from DEPLOYMENT_WHITELIST_LIST where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-system/src/main/resources/mapper/system/deployment/SysApkInfoMapper.xml b/sf-system/src/main/resources/mapper/system/deployment/SysApkInfoMapper.xml new file mode 100644 index 0000000..d741c83 --- /dev/null +++ b/sf-system/src/main/resources/mapper/system/deployment/SysApkInfoMapper.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select id, app_id,app_code, uploading_type, sys_apk_name, version, sys_apk, sys_apk_size, sys_type, uploading_status, uploading_log_id, order_num, is_delete, created, modified, create_time, update_time from DEPLOYMENT_APK_INFO + + + + + + + + insert into DEPLOYMENT_APK_INFO + + uploading_type, + sys_apk_name, + app_id, + app_code, + version, + sys_apk, + sys_apk_size, + sys_type, + uploading_status, + uploading_log_id, + order_num, + is_delete, + created, + modified, + create_time, + update_time, + + + #{uploadingType}, + #{sysApkName}, + #{appId}, + #{appCode}, + #{version}, + #{sysApk}, + #{sysApkSize}, + #{sysType}, + #{uploadingStatus}, + #{uploadingLogId}, + #{orderNum}, + #{isDelete}, + #{created}, + #{modified}, + #{createTime}, + #{updateTime}, + + + + + update DEPLOYMENT_APK_INFO + + uploading_type = #{uploadingType}, + sys_apk_name = #{sysApkName}, + app_id = #{appId}, + app_code = #{appCode}, + version = #{version}, + sys_apk = #{sysApk}, + sys_apk_size = #{sysApkSize}, + sys_type = #{sysType}, + uploading_status = #{uploadingStatus}, + uploading_log_id = #{uploadingLogId}, + order_num = #{orderNum}, + is_delete = #{isDelete}, + created = #{created}, + modified = #{modified}, + create_time = #{createTime}, + update_time = #{updateTime}, + + where id = #{id} + + + + delete from DEPLOYMENT_APK_INFO where id = #{id} + + + + delete from SYS_APK_INFO where id in + + #{id} + + + \ No newline at end of file diff --git a/sf-ui/Dockerfile b/sf-ui/Dockerfile new file mode 100644 index 0000000..0a63bc2 --- /dev/null +++ b/sf-ui/Dockerfile @@ -0,0 +1,7 @@ +FROM registry.zsmarter.com/public/nginx:1.22.1 +COPY ./sf-ui/dist /app/nginx/html +COPY ./sf-ui/nginx.conf /etc/nginx/nginx.conf +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + + + diff --git a/sf-ui/nginx.conf b/sf-ui/nginx.conf new file mode 100644 index 0000000..63ce459 --- /dev/null +++ b/sf-ui/nginx.conf @@ -0,0 +1,47 @@ + +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + client_max_body_size 1024m; + + #gzip on; + + server { + listen 80; + server_name localhost; + charset utf-8; + + location / { + root /app/nginx/html; + try_files $uri $uri/ @router; + index index.html index.htm; + } + + location @router { + rewrite ^.*$ /index.html last; + } + + location /stage-api/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header REMOTE-HOST $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://zt-sac-java:7781/; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + + } + +} diff --git a/sf-ui/src/api/FDS/whiteList.js b/sf-ui/src/api/FDS/whiteList.js index 96b925c..0999bef 100644 --- a/sf-ui/src/api/FDS/whiteList.js +++ b/sf-ui/src/api/FDS/whiteList.js @@ -1,13 +1,44 @@ import request from '@/utils/request' - - -export function Document11691998078000list(query) { +// 查询白名单列表列表 +export function listWhitelist (query) { return request({ - url: '/forms/api/router/get/Document11691998078000list', + url: '/deployment/whitelist/list', method: 'get', params: query }) } +// 查询白名单列表详细 +export function getWhitelist (id) { + return request({ + url: '/deployment/whitelist/' + id, + method: 'get' + }) +} +// 新增白名单列表 +export function addWhitelist (data) { + return request({ + url: '/deployment/whitelist', + method: 'post', + data: data + }) +} + +// 修改白名单列表 +export function updateWhitelist (data) { + return request({ + url: '/deployment/whitelist', + method: 'put', + data: data + }) +} + +// 删除白名单列表 +export function delWhitelist (id) { + return request({ + url: '/deployment/whitelist/' + id, + method: 'delete' + }) +} diff --git a/sf-ui/src/api/deployment/module.js b/sf-ui/src/api/deployment/module.js new file mode 100644 index 0000000..d28af8e --- /dev/null +++ b/sf-ui/src/api/deployment/module.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询H5模块包列表 +export function listModule(query) { + return request({ + url: '/deployment/module/list', + method: 'get', + params: query + }) +} + +// 查询H5模块包详细 +export function getModule(id) { + return request({ + url: '/deployment/module/' + id, + method: 'get' + }) +} + +// 新增H5模块包 +export function addModule(data) { + return request({ + url: '/deployment/module', + method: 'post', + data: data + }) +} + +// 修改H5模块包 +export function updateModule(data) { + return request({ + url: '/deployment/module', + method: 'put', + data: data + }) +} + +// 删除H5模块包 +export function delModule(id) { + return request({ + url: '/deployment/module/' + id, + method: 'delete' + }) +} diff --git a/sf-ui/src/api/deployment/publish.js b/sf-ui/src/api/deployment/publish.js index a944419..10545af 100644 --- a/sf-ui/src/api/deployment/publish.js +++ b/sf-ui/src/api/deployment/publish.js @@ -42,3 +42,19 @@ export function delPublish(id) { method: 'delete' }) } + +// 发布 +export function publish(id) { + return request({ + url: '/deployment/publish/publish/' + id, + method: 'get' + }) +} + +// 下架 +export function soldPublish(id) { + return request({ + url: '/deployment/publish/sold/out/' + id, + method: 'get' + }) +} diff --git a/sf-ui/src/components/DictTag/index.vue b/sf-ui/src/components/DictTag/index.vue index a0fd66f..c583e95 100644 --- a/sf-ui/src/components/DictTag/index.vue +++ b/sf-ui/src/components/DictTag/index.vue @@ -5,19 +5,19 @@ {{ item.label + " " }}{{ item.label + ' ' }} - {{ item.label + " " }} + {{ item.label + ' ' }} @@ -29,61 +29,63 @@ + diff --git a/sf-ui/src/layout/components/Navbar.vue b/sf-ui/src/layout/components/Navbar.vue index 67a53ab..7506ba1 100644 --- a/sf-ui/src/layout/components/Navbar.vue +++ b/sf-ui/src/layout/components/Navbar.vue @@ -1,5 +1,8 @@