diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java index 47195001..4ce8a870 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java +++ b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java @@ -69,5 +69,5 @@ public interface ErrorCodeConstants { ErrorCode DEMO03_STUDENT_NOT_EXISTS = new ErrorCode(1_001_201_007, "学生不存在"); ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_008, "学生班级不存在"); ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_009, "学生班级已存在"); - + ErrorCode OA_QRCODE_ERROR = new ErrorCode(1_009_301_001, "小程序码生成错误!"); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java index 13c24718..20b80f56 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java @@ -3,17 +3,16 @@ package cn.iocoder.yudao.module.infra.controller.admin.file; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; +import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.BpmFileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.BusinessFileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.UserFileDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.file.*; import cn.iocoder.yudao.module.infra.service.file.FileService; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -29,8 +28,13 @@ import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.OA_QRCODE_ERROR; @Tag(name = "管理后台 - 文件存储") @RestController @@ -42,6 +46,9 @@ public class FileController { @Resource private FileService fileService; + @Resource + private SocialClientApi socialClientApi; + @PostMapping("/uploadBpmFileProcessInstanceId") @Operation(summary = "更新文件的流程实例ID") @OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要 @@ -121,6 +128,7 @@ public class FileController { @PostMapping("/businessUpload") @Operation(summary = "上传业务类型附件【如:工作日/周报附件】") @OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要 + @PermitAll public CommonResult businessUpload(@RequestParam("uploadFiles") MultipartFile file,@RequestParam("businessType")Long businessType) throws Exception { return success(fileService.createBusinessReturnFile(file,businessType)); } @@ -128,6 +136,7 @@ public class FileController { @DeleteMapping("/deleteBusinessFile") @Operation(summary = "删除业务流附件【如:工作日/周报附件】") @Parameter(name = "url", description = "附件URL地址", required = true) + @PermitAll public CommonResult deleteBusinessFile(@RequestParam("url") String url) throws Exception { fileService.deleteBusinessFile(url); return success(true); @@ -145,6 +154,7 @@ public class FileController { @PostMapping("/userFileUpload") @Operation(summary = "上传用户文件") @OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要 + @PermitAll public CommonResult userFileUpload(@RequestParam("uploadFiles") MultipartFile file) { return success(fileService.createUserReturnFile(file)); } @@ -156,4 +166,46 @@ public class FileController { fileService.deleteUserFile(url); return success(true); } + + @GetMapping("/create-QRCode") + @Operation(summary = "获得入职申请小程序码") + @Parameter(name = "userId", description = "发起人用户编号", required = true, example = "1024") + @Parameter(name = "deptId", description = "部门编号", required = true, example = "1024") + @Parameter(name = "postId", description = "岗位编号", example = "1024") + public CommonResult getQRCode(@RequestParam("userId") Long userId, + @RequestParam("deptId") Long deptId, + @RequestParam(value = "postId", required = false) Long postId) { + + // 查询当前部门编号下 是否存在小程序码 + QRCodeDO qrCodeDO = fileService.getQRCode(deptId); + + // 小程序码不存在,则 生成新的小程序码 + if (qrCodeDO == null) { + + // 生成小程序码 + File QRCode = socialClientApi.getQRCode("subPages/task/myTask", "id=").getCheckedData(); + + JSONObject object = new JSONObject(); + object.set("userId", userId); + object.set("deptId", deptId); + object.set("postId", postId); + + try { + FileInputStream fis = new FileInputStream(QRCode); + byte[] content = IoUtil.readBytes(fis); + + // 上传小程序码 获得url + qrCodeDO = fileService.createQRCodeFile(deptId, QRCode.getName(), content, object.toString()); + + } catch (IOException e) { + + return error(OA_QRCODE_ERROR); + } + }else { // 存在的时候, 则更新小程序码的生成时间 + + fileService.updateQRCodeFile(qrCodeDO.getId()); + } + + return success(qrCodeDO); + } } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/QRCodeDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/QRCodeDO.java new file mode 100644 index 00000000..85632820 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/QRCodeDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.file; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +/** + * 文件表 + * 每次文件上传,都会记录一条记录到该表中 + * + */ +@TableName("system_qr_code") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class QRCodeDO extends BaseDO { + + /** + * 编号,数据库自增 + */ + private Long id; + + /** + * 业务编号 + */ + private String businessId; + + /** + * 业务类型 + * 1 入职申请小程序码 + */ + private Integer businessType; + + /** + * 配置编号 + * 关联 {@link FileConfigDO#getId()} + */ + private Long configId; + + /** + * 文件路径 + */ + private String url; + + /** + * 参数值 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private String scene; +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/QRCodeDOMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/QRCodeDOMapper.java new file mode 100644 index 00000000..2b79a5f5 --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/QRCodeDOMapper.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.file; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.file.QRCodeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface QRCodeDOMapper extends BaseMapperX { + + default QRCodeDO selectByDeptId(Long deptId) { + + return selectOne(new LambdaQueryWrapperX() + .eq(QRCodeDO::getBusinessId, deptId) + .eq(QRCodeDO::getBusinessType, 1)); + } +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/rpc/config/RpcConfiguration.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/rpc/config/RpcConfiguration.java index a47ca94d..cd267603 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/rpc/config/RpcConfiguration.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/rpc/config/RpcConfiguration.java @@ -1,11 +1,12 @@ package cn.iocoder.yudao.module.infra.framework.rpc.config; import cn.iocoder.yudao.module.system.api.equipment.AttendanceMachineApi; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@EnableFeignClients(clients = { AdminUserApi.class, AttendanceMachineApi.class }) +@EnableFeignClients(clients = { AdminUserApi.class, AttendanceMachineApi.class, SocialClientApi.class }) public class RpcConfiguration { } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java index 6cea8ece..7342f6b0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java @@ -5,10 +5,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.BusinessFileU import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.UserFileUpdateReqVO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.BpmFileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.BusinessFileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.UserFileDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.file.*; import org.springframework.web.multipart.MultipartFile; /** @@ -149,4 +146,25 @@ public interface FileService { * @param updateReqVO 更新信息 */ void updateUserFileUserId(UserFileUpdateReqVO updateReqVO); + + /** + * 保存业务类型的附件 + * @param name 文件名 + * @param content 字节流 + * @return url + */ + QRCodeDO createQRCodeFile(Long deptId, String name, byte[] content, String scene) ; + + /** + * 获得小程序码信息 + * @param deptId 部门编号 + * @return 小程序码信息 + */ + QRCodeDO getQRCode(Long deptId); + + /** + * 更新 小程序码生成时间 + * @param id 编号 + */ + void updateQRCodeFile(Long id); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java index 561332e7..c0588ad7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java @@ -13,10 +13,7 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.BpmFileUpload import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.BusinessFileUploadReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.UserFileUpdateReqVO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.BpmFileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.BusinessFileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.file.UserFileDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.file.*; import cn.iocoder.yudao.module.infra.dal.mysql.file.*; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.SneakyThrows; @@ -24,6 +21,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import java.time.LocalDateTime; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -52,6 +50,9 @@ public class FileServiceImpl implements FileService { @Resource private UserFileMapper userFileMapper; + @Resource + private QRCodeDOMapper qrCodeDOMapper; + @Override public PageResult getFilePage(FilePageReqVO pageReqVO) { return fileMapper.selectPage(pageReqVO); @@ -468,4 +469,50 @@ public class FileServiceImpl implements FileService { // 调用 MyBatis Plus 的 update 方法执行批量更新 userFileMapper.update(null, lambdaUpdateWrapper); } + + @Override + @SneakyThrows + public QRCodeDO createQRCodeFile(Long deptId, String name, byte[] content, String scene) { + + long timestamp = System.currentTimeMillis(); + // 计算默认的 path 名 + String type = FileTypeUtils.getMineType(content, name); + String path = deptId + "_" + timestamp; + String beginPath = name.replace(".", "_"); + path = beginPath + "_" + path + "." + FileNameUtil.extName(name); + // 如果 name 为空,则使用 path 填充 + if (StrUtil.isEmpty(name)) { + name = path; + } + // 上传到文件存储器 + FileClient client = fileConfigService.getMasterFileClient(); + Assert.notNull(client, "客户端(master) 不能为空"); + String url = client.upload(content, path, type); + + // 插入 system_qr_code + QRCodeDO fileDo = new QRCodeDO(); + fileDo.setBusinessId(deptId.toString()); + fileDo.setBusinessType(1); + fileDo.setConfigId(client.getId()); + fileDo.setUrl(url); + fileDo.setScene(scene); + qrCodeDOMapper.insert(fileDo); + + return fileDo; + } + + @Override + public QRCodeDO getQRCode(Long deptId) { + + return qrCodeDOMapper.selectByDeptId(deptId); + } + + @Override + public void updateQRCodeFile(Long id) { + + QRCodeDO updateDO = new QRCodeDO(); + updateDO.setId(id); + updateDO.setUpdateTime(LocalDateTime.now()); + qrCodeDOMapper.updateById(updateDO); + } } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java index 8cd4e921..90baa3b1 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java @@ -12,6 +12,8 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; +import java.io.File; + @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - 社交应用") public interface SocialClientApi { @@ -47,4 +49,10 @@ public interface SocialClientApi { CommonResult getWxMaPhoneNumberInfo(@RequestParam("userType") Integer userType, @RequestParam("phoneCode") String phoneCode); + @GetMapping(PREFIX + "/getQRCode") + @Operation(summary = "获得微信小程序码") + @Parameter(name = "path", description = "地址", required = true, example = "/subPages/register/register") + @Parameter(name = "scene", description = "参数", required = true, example = "a=1") + CommonResult getQRCode(@RequestParam("path") String path, + @RequestParam("scene") String scene); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java index c4d9314a..0c0e431c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java @@ -1,17 +1,21 @@ package cn.iocoder.yudao.module.system.api.social; +import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.social.dto.SocialWxJsapiSignatureRespDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO; import cn.iocoder.yudao.module.system.service.social.SocialClientService; +import lombok.SneakyThrows; import me.chanjar.weixin.common.bean.WxJsapiSignature; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; +import java.io.File; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** @@ -43,4 +47,13 @@ public class SocialClientApiImpl implements SocialClientApi { return success(BeanUtils.toBean(info, SocialWxPhoneNumberInfoRespDTO.class)); } + @Override + @SneakyThrows + public CommonResult getQRCode(String path, String scene) { + + WxMaService wxService = socialClientService.getWxMaService(); + File QRCode = wxService.getQrcodeService().createWxaCodeUnlimit(scene, path); + + return success(QRCode); + } }