├── assets ├── 1.png ├── 10.png ├── 11.png ├── xcx.png ├── 1727272391326.jpg ├── 1727272413410.jpg ├── 1727272724832.jpg ├── 1727273729517.jpg ├── 1727274177075.jpg ├── 1727274232466.jpg └── 11426e6d3f99567d3715c843ad999c4.jpg ├── src └── main │ ├── java │ └── org │ │ └── photo │ │ ├── modular │ │ ├── business │ │ │ ├── service │ │ │ │ ├── CustomService.java │ │ │ │ ├── WebSetService.java │ │ │ │ ├── PhotoRecordService.java │ │ │ │ ├── PhotoService.java │ │ │ │ ├── UploadService.java │ │ │ │ ├── ItemService.java │ │ │ │ ├── impl │ │ │ │ │ ├── CustomServiceImpl.java │ │ │ │ │ ├── WebSetServiceImpl.java │ │ │ │ │ ├── PhotoRecordServiceImpl.java │ │ │ │ │ ├── PhotoServiceImpl.java │ │ │ │ │ ├── ItemServiceImpl.java │ │ │ │ │ ├── FilePartDetailService.java │ │ │ │ │ ├── UploadServiceImpl.java │ │ │ │ │ ├── FileDetailService.java │ │ │ │ │ └── ApiServiceImpl.java │ │ │ │ └── ApiService.java │ │ │ ├── dao │ │ │ │ ├── ItemDao.java │ │ │ │ ├── PhotoDao.java │ │ │ │ ├── CustomDao.java │ │ │ │ ├── FilePartDetailMapper.java │ │ │ │ ├── FileDetailMapper.java │ │ │ │ ├── PhotoRecordDao.java │ │ │ │ └── WebSetDao.java │ │ │ ├── model │ │ │ │ ├── vo │ │ │ │ │ ├── WxLoginVo.java │ │ │ │ │ ├── PicVo.java │ │ │ │ │ └── PhotoVo.java │ │ │ │ ├── dto │ │ │ │ │ └── CreatePhotoDto.java │ │ │ │ └── entity │ │ │ │ │ ├── PhotoRecord.java │ │ │ │ │ ├── WebSet.java │ │ │ │ │ ├── Item.java │ │ │ │ │ ├── Custom.java │ │ │ │ │ ├── Photo.java │ │ │ │ │ ├── FilePartDetail.java │ │ │ │ │ └── FileDetail.java │ │ │ ├── file │ │ │ │ └── ImageUpload.java │ │ │ └── controller │ │ │ │ ├── ItemController.java │ │ │ │ ├── ApiController.java │ │ │ │ └── UploadController.java │ │ ├── user │ │ │ ├── service │ │ │ │ ├── ICustomerUserService.java │ │ │ │ ├── IWechatUserService.java │ │ │ │ └── impl │ │ │ │ │ ├── CustomerUserServiceImpl.java │ │ │ │ │ └── WechatUserServiceImpl.java │ │ │ ├── mapper │ │ │ │ ├── CustomerUserMapper.java │ │ │ │ └── WechatUserMapper.java │ │ │ ├── dto │ │ │ │ └── WechatUserInfoDto.java │ │ │ ├── vo │ │ │ │ └── CustomerUserVo.java │ │ │ ├── entity │ │ │ │ ├── WechatUser.java │ │ │ │ └── CustomerUser.java │ │ │ └── contorller │ │ │ │ └── CustomerUserController.java │ │ ├── sys │ │ │ ├── service │ │ │ │ ├── ISysAppService.java │ │ │ │ └── impl │ │ │ │ │ └── SysAppServiceImpl.java │ │ │ ├── mapper │ │ │ │ └── SysAppMapper.java │ │ │ └── entity │ │ │ │ └── SysApp.java │ │ ├── HivisionIDPhotos │ │ │ ├── request │ │ │ │ ├── HIDHumanMattingRequest.java │ │ │ │ ├── HIDSetKbRequest.java │ │ │ │ ├── HIDAddBackgroundRequest.java │ │ │ │ ├── HIDGenerateLayoutPhotosRequest.java │ │ │ │ ├── HIDWatermarkRequest.java │ │ │ │ ├── HIDIdPhotoCropRequest.java │ │ │ │ └── HIDIdPhotoRequest.java │ │ │ ├── response │ │ │ │ └── HIDHivisionResponse.java │ │ │ ├── enums │ │ │ │ └── HIDOptTypeEnum.java │ │ │ ├── service │ │ │ │ ├── HivisionIDPhotosService.java │ │ │ │ └── impl │ │ │ │ │ └── HivisionIDPhotosServiceImpl.java │ │ │ └── contorller │ │ │ │ └── HivisionIDPhotosController.java │ │ └── wechat │ │ │ ├── service │ │ │ ├── IWechatService.java │ │ │ └── impl │ │ │ │ └── WechatServiceImpl.java │ │ │ ├── dto │ │ │ └── AccessTokenDto.java │ │ │ ├── params │ │ │ ├── LoginXcxParams.java │ │ │ └── LoginH5Params.java │ │ │ └── contorller │ │ │ ├── CheckController.java │ │ │ └── WechatController.java │ │ ├── IDPhotoWechatApp.java │ │ ├── exception │ │ ├── BizException.java │ │ └── BizExceptionHandler.java │ │ ├── config │ │ ├── MybatisPlusConfig.java │ │ ├── CorsFilter.java │ │ ├── MyMetaObjectHandler.java │ │ └── SaTokenConfigure.java │ │ ├── task │ │ └── PhotoTask.java │ │ └── util │ │ ├── R.java │ │ ├── RedisUtils.java │ │ ├── ServletUtils.java │ │ ├── HttpClient.java │ │ └── StringUtils.java │ └── resources │ ├── application.yml │ └── application-dev.yml ├── .gitignore ├── README.md ├── pom.xml └── LICENSE /assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1.png -------------------------------------------------------------------------------- /assets/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/10.png -------------------------------------------------------------------------------- /assets/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/11.png -------------------------------------------------------------------------------- /assets/xcx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/xcx.png -------------------------------------------------------------------------------- /assets/1727272391326.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1727272391326.jpg -------------------------------------------------------------------------------- /assets/1727272413410.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1727272413410.jpg -------------------------------------------------------------------------------- /assets/1727272724832.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1727272724832.jpg -------------------------------------------------------------------------------- /assets/1727273729517.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1727273729517.jpg -------------------------------------------------------------------------------- /assets/1727274177075.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1727274177075.jpg -------------------------------------------------------------------------------- /assets/1727274232466.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/1727274232466.jpg -------------------------------------------------------------------------------- /assets/11426e6d3f99567d3715c843ad999c4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wmlcjj/AI-IDPhoto/HEAD/assets/11426e6d3f99567d3715c843ad999c4.jpg -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/CustomService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.business.model.entity.Custom; 5 | 6 | public interface CustomService extends IService { 7 | 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/WebSetService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.business.model.entity.WebSet; 5 | 6 | public interface WebSetService extends IService { 7 | 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/PhotoRecordService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.business.model.entity.PhotoRecord; 5 | 6 | public interface PhotoRecordService extends IService { 7 | 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/ItemDao.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import org.photo.modular.business.model.entity.Item; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface ItemDao extends BaseMapper { 8 | 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/PhotoDao.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import org.photo.modular.business.model.entity.Photo; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface PhotoDao extends BaseMapper { 8 | 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/CustomDao.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import org.photo.modular.business.model.entity.Custom; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface CustomDao extends BaseMapper { 8 | 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/FilePartDetailMapper.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.photo.modular.business.model.entity.FilePartDetail; 6 | 7 | @Mapper 8 | public interface FilePartDetailMapper extends BaseMapper {} 9 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/FileDetailMapper.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.photo.modular.business.model.entity.FileDetail; 6 | 7 | @Mapper 8 | public interface FileDetailMapper extends BaseMapper { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/service/ICustomerUserService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.business.model.vo.WxLoginVo; 5 | import org.photo.modular.user.entity.CustomerUser; 6 | 7 | public interface ICustomerUserService extends IService { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/PhotoRecordDao.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import org.photo.modular.business.model.entity.PhotoRecord; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface PhotoRecordDao extends BaseMapper { 8 | 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/mapper/CustomerUserMapper.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.photo.modular.user.entity.CustomerUser; 6 | 7 | @Mapper 8 | public interface CustomerUserMapper extends BaseMapper { 9 | 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/sys/service/ISysAppService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.sys.service; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import org.photo.modular.sys.entity.SysApp; 6 | 7 | /** 8 | *

${Description}

9 | * 10 | * @author chenjiajun 11 | * @date 2024/2/1 16:42 12 | */ 13 | public interface ISysAppService extends IService { 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/PhotoService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.business.model.entity.Photo; 5 | 6 | import java.util.List; 7 | 8 | public interface PhotoService extends IService { 9 | 10 | List photoList(int pageNum, int pageSize,String userId); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/UploadService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import org.springframework.web.multipart.MultipartFile; 4 | import org.photo.util.R; 5 | 6 | public interface UploadService { 7 | 8 | //图片鉴黄 9 | String checkNsfw(MultipartFile multipartFile); 10 | 11 | //将流转成base64 12 | R uploadPhoto(MultipartFile file, String originalFilename); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/service/IWechatUserService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.user.entity.WechatUser; 5 | 6 | /** 7 | * @Description: wechat_user 8 | * @Author: jeecg-boot 9 | * @Date: 2022-10-04 10 | * @Version: V1.0 11 | */ 12 | public interface IWechatUserService extends IService { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/sys/mapper/SysAppMapper.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.sys.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.photo.modular.sys.entity.SysApp; 6 | 7 | /** 8 | * (SysApp)表数据库访问层 9 | * 10 | * @author bob 11 | * @since 2021-04-13 15:37:46 12 | */ 13 | @Mapper 14 | public interface SysAppMapper extends BaseMapper { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/ItemService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.photo.modular.business.model.entity.Item; 5 | 6 | import java.util.List; 7 | 8 | public interface ItemService extends IService { 9 | 10 | //尺寸列表 11 | List itemList(int pageNum, int pageSize, int type, String userId,String name); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDHumanMattingRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDHumanMattingRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String human_matting_model; // 人像分割模型,默认为modnet_photographic_portrait_matting 10 | private Integer dpi; // 图像分辨率,默认为300 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDSetKbRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDSetKbRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String input_image_base64; // 传入的图像文件的base64编码 10 | private Integer kb; // 输出照片的 KB 值,默认为None 11 | private Integer dpi; // 图像分辨率,默认为300 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/dao/WebSetDao.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.dao; 2 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 | import org.apache.ibatis.annotations.Select; 4 | import org.photo.modular.business.model.entity.WebSet; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | @Mapper 8 | public interface WebSetDao extends BaseMapper { 9 | 10 | 11 | @Select("SELECT download_one,download_two,video_unit_id FROM web_set LIMIT 1") 12 | WebSet getWeb(); 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/vo/WxLoginVo.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | /* 11 | * 小程序登录用的,微信官方数据+token封装+状态码/错误信息 12 | * 13 | * */ 14 | public class WxLoginVo { 15 | private String token; 16 | private String sessionKey; 17 | private String openid; 18 | //头像或昵称是否为空 19 | private Integer isExtendNull; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/service/IWechatService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.service; 2 | 3 | import org.photo.modular.user.dto.WechatUserInfoDto; 4 | import org.photo.modular.user.vo.CustomerUserVo; 5 | import org.photo.modular.wechat.params.LoginH5Params; 6 | import org.photo.modular.wechat.params.LoginXcxParams; 7 | 8 | /** 9 | * 微信相关服务 10 | */ 11 | public interface IWechatService { 12 | 13 | CustomerUserVo loginXcx(LoginXcxParams params); 14 | CustomerUserVo loginH5(LoginH5Params params); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/dto/AccessTokenDto.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Description: accessToken 9 | * @Author: chenjiajun 10 | * @Date: 2022-10-01 11 | * @Version: V1.0 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class AccessTokenDto { 17 | 18 | String access_token; 19 | Integer expires_in; 20 | String refresh_token; 21 | String openid; 22 | String scope; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/CustomServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.photo.modular.business.dao.CustomDao; 5 | import org.photo.modular.business.model.entity.Custom; 6 | import org.photo.modular.business.service.CustomService; 7 | import org.springframework.stereotype.Service; 8 | 9 | 10 | @Service 11 | public class CustomServiceImpl extends ServiceImpl implements CustomService { 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/WebSetServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.photo.modular.business.dao.WebSetDao; 5 | import org.photo.modular.business.model.entity.WebSet; 6 | import org.photo.modular.business.service.WebSetService; 7 | import org.springframework.stereotype.Service; 8 | 9 | 10 | @Service 11 | public class WebSetServiceImpl extends ServiceImpl implements WebSetService { 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/mapper/WechatUserMapper.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.photo.modular.user.entity.WechatUser; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @Description: wechat_user 10 | * @Author: jeecg-boot 11 | * @Date: 2022-10-04 12 | * @Version: V1.0 13 | */ 14 | @Component 15 | @Mapper 16 | public interface WechatUserMapper extends BaseMapper { 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/photo/IDPhotoWechatApp.java: -------------------------------------------------------------------------------- 1 | package org.photo; 2 | 3 | import org.dromara.x.file.storage.spring.EnableFileStorage; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.scheduling.annotation.EnableScheduling; 7 | 8 | @SpringBootApplication 9 | @EnableScheduling 10 | @EnableFileStorage 11 | public class IDPhotoWechatApp { 12 | public static void main(String[] args) { 13 | SpringApplication.run(IDPhotoWechatApp.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/ApiService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service; 2 | 3 | import org.photo.modular.business.model.dto.CreatePhotoDto; 4 | import org.photo.modular.business.model.vo.PicVo; 5 | 6 | public interface ApiService { 7 | 8 | 9 | //生成证件照,初始化,返回原图(用于下载高清),蓝图(用于初始化页面),透明图(用于切换颜色) 10 | PicVo createIdPhoto(CreatePhotoDto createPhotoDto); 11 | 12 | //换背景色 13 | PicVo updateIdPhoto(CreatePhotoDto createPhotoDto); 14 | 15 | 16 | //更新用户保存记录 17 | PicVo updateUserPhonto(String userid,String img,Integer photoId); 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/service/impl/CustomerUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.photo.modular.user.entity.CustomerUser; 5 | import org.photo.modular.user.mapper.CustomerUserMapper; 6 | import org.photo.modular.user.service.ICustomerUserService; 7 | import org.springframework.stereotype.Service; 8 | 9 | 10 | @Service 11 | public class CustomerUserServiceImpl extends ServiceImpl implements ICustomerUserService { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDAddBackgroundRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDAddBackgroundRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String input_image_base64; // 传入的图像文件的base64编码 10 | private String color; // 背景色HEX值,默认为000000 11 | private Integer kb; // 输出照片的 KB 值,默认为None 12 | private Integer render; // 渲染模式,默认为0 13 | private Integer dpi; // 图像分辨率,默认为300 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/PhotoRecordServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.photo.modular.business.dao.PhotoRecordDao; 5 | import org.photo.modular.business.model.entity.PhotoRecord; 6 | import org.photo.modular.business.service.PhotoRecordService; 7 | import org.springframework.stereotype.Service; 8 | 9 | 10 | @Service 11 | public class PhotoRecordServiceImpl extends ServiceImpl implements PhotoRecordService { 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDGenerateLayoutPhotosRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDGenerateLayoutPhotosRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String input_image_base64; // 传入的图像文件的base64编码 10 | private Integer height; // 输入图像的高度,默认为413 11 | private Integer width; // 输入图像的宽度,默认为295 12 | private Integer kb; // 输出照片的 KB 值,默认为None 13 | private Integer dpi; // 图像分辨率,默认为300 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/photo/exception/BizException.java: -------------------------------------------------------------------------------- 1 | package org.photo.exception; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | /** 7 | * 业务异常基类 8 | */ 9 | public class BizException extends RuntimeException implements Serializable { 10 | 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | public BizException(String message){ 15 | super(message); 16 | } 17 | 18 | public BizException(Throwable cause) 19 | { 20 | super(cause); 21 | } 22 | 23 | public BizException(String message, Throwable cause) 24 | { 25 | super(message,cause); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/sys/service/impl/SysAppServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.sys.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.photo.modular.sys.entity.SysApp; 5 | import org.photo.modular.sys.mapper.SysAppMapper; 6 | import org.photo.modular.sys.service.ISysAppService; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

${Description}

11 | * 12 | * @author chenjiajun 13 | * @date 2024/2/1 16:43 14 | */ 15 | @Service 16 | public class SysAppServiceImpl extends ServiceImpl implements ISysAppService { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/vo/PicVo.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.vo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class PicVo { 11 | //原始图片 (目前不作为原始图片使用,存放的高清抠图) 12 | private String oImg; 13 | //高清抠图透明图片 14 | private String kImg; 15 | //已经上色图片 16 | private String cImg; 17 | //错误消息 18 | private String msg; 19 | //附加参数 20 | private Integer id2; 21 | //图片地址 22 | private String picUrl; 23 | //图片id 24 | private String picId; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/dto/CreatePhotoDto.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class CreatePhotoDto { 11 | private String image; 12 | private Integer height; 13 | private Integer width; 14 | private String colors; 15 | 16 | //规格id 17 | private Integer itemId; 18 | //记录id 19 | private Integer photoId; 20 | 21 | private String userId; 22 | private Integer type; 23 | 24 | //操作类型 25 | private Integer optType; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDWatermarkRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDWatermarkRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String input_image_base64; // 传入的图像文件的base64编码 10 | private String text; // 水印文本,默认为Hello 11 | private Integer size; // 水印字体大小,默认为20 12 | private Float opacity; // 水印透明度,默认为0.5 13 | private Integer angle; // 水印旋转角度,默认为30 14 | private String color; // 水印颜色,默认为#000000 15 | private Integer space; // 水印间距,默认为25 16 | private Integer dpi; // 图像分辨率,默认为300 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store 39 | /.idea/ 40 | /src/main/resources/application-test.yml 41 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/params/LoginXcxParams.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.params; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Description: 小程序登陆参数 9 | * @Author: chenjiajun 10 | * @Date: 2022-10-01 11 | * @Version: V1.0 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class LoginXcxParams { 17 | 18 | private String appId; 19 | 20 | private String code; 21 | 22 | private String nickname; 23 | 24 | private Integer sex; 25 | 26 | private String province; 27 | 28 | private String city; 29 | 30 | private String country; 31 | 32 | private String headimgurl; 33 | 34 | private String unionid; 35 | 36 | private String language; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/photo/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package org.photo.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class MybatisPlusConfig { 11 | @Bean 12 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 13 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 14 | interceptor.addInnerInterceptor(new 15 | PaginationInnerInterceptor(DbType.MYSQL)); 16 | return interceptor; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/photo/exception/BizExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package org.photo.exception; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.photo.util.R; 5 | import org.springframework.web.bind.annotation.ExceptionHandler; 6 | import org.springframework.web.bind.annotation.RestControllerAdvice; 7 | 8 | /** 9 | * 异常处理器 10 | * 11 | * @Author scott 12 | * @Date 2019 13 | */ 14 | @RestControllerAdvice 15 | @Slf4j 16 | public class BizExceptionHandler { 17 | 18 | 19 | /** 20 | * 处理自定义异常 21 | */ 22 | @ExceptionHandler(BizException.class) 23 | public R handleBizException(BizException e){ 24 | return R.no(e.getMessage()); 25 | } 26 | 27 | @ExceptionHandler(Exception.class) 28 | public R handleException(Exception e){ 29 | return R.no("操作失败,"+e.getMessage()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/photo/task/PhotoTask.java: -------------------------------------------------------------------------------- 1 | package org.photo.task; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.scheduling.annotation.Scheduled; 6 | import org.springframework.stereotype.Component; 7 | import org.photo.modular.business.model.entity.Photo; 8 | import org.photo.modular.business.service.PhotoService; 9 | 10 | @Component 11 | public class PhotoTask { 12 | @Autowired 13 | private PhotoService photoService; 14 | 15 | //每天00:00删除仅生成未保存的数据 16 | @Scheduled(cron = "0 0 0 * * ?") 17 | public void executeTask() { 18 | QueryWrapper qw = new QueryWrapper<>(); 19 | qw.isNull("n_img"); 20 | photoService.remove(qw); 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/params/LoginH5Params.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.params; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Description: 小程序登陆参数 9 | * @Author: chenjiajun 10 | * @Date: 2022-10-01 11 | * @Version: V1.0 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class LoginH5Params { 17 | 18 | private String appId; 19 | 20 | private String code; 21 | 22 | private String state; 23 | 24 | private String jumpUrl; 25 | 26 | private String nickname; 27 | 28 | private Integer sex; 29 | 30 | private String province; 31 | 32 | private String city; 33 | 34 | private String country; 35 | 36 | private String headimgurl; 37 | 38 | private String unionid; 39 | 40 | private String language; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/PhotoRecord.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | import java.util.Date; 3 | 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | @TableName("photo_record") 15 | public class PhotoRecord { 16 | /** 17 | * 用户行为记录 18 | */ 19 | @TableId(type = IdType.AUTO) 20 | private Integer id; 21 | /** 22 | * 名字 23 | */ 24 | private String name; 25 | /** 26 | * 用户id 27 | */ 28 | private String userId; 29 | /** 30 | * 创建时间 31 | */ 32 | private Date createTime; 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/service/impl/WechatUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.photo.modular.user.entity.WechatUser; 6 | import org.photo.modular.user.mapper.WechatUserMapper; 7 | import org.photo.modular.user.service.IWechatUserService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * @Description: wechat_user 13 | * @Author: jeecg-boot 14 | * @Date: 2022-10-04 15 | * @Version: V1.0 16 | */ 17 | @Slf4j 18 | @Service 19 | public class WechatUserServiceImpl extends ServiceImpl implements IWechatUserService { 20 | 21 | @Autowired 22 | private WechatUserMapper wechatUserMapper; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDIdPhotoCropRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDIdPhotoCropRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String input_image_base64; // 传入的图像文件的base64编码 10 | private Integer height; // 标准证件照高度,默认为413 11 | private Integer width; // 标准证件照宽度,默认为295 12 | private String face_detect_model; // 人脸检测模型,默认为mtcnn 13 | private Boolean hd; // 是否生成高清证件照,默认为true 14 | private Integer dpi; // 图像分辨率,默认为300 15 | private Float head_measure_ratio; // 面部面积与照片面积的比例,默认为0.2 16 | private Float head_height_ratio; // 面部中心与照片顶部的高度比例,默认为0.45 17 | private Float top_distance_max; // 头部与照片顶部距离的比例最大值,默认为0.12 18 | private Float top_distance_min; // 头部与照片顶部距离的比例最小值,默认为0.1 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/dto/WechatUserInfoDto.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Description: accessToken 9 | * @Author: chenjiajun 10 | * @Date: 2022-10-01 11 | * @Version: V1.0 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class WechatUserInfoDto { 17 | 18 | private String appId; 19 | 20 | private String unionid; 21 | 22 | private String openid; 23 | 24 | private String nickname; 25 | 26 | private Integer sex; 27 | 28 | private String province; 29 | 30 | private String city; 31 | 32 | private String country; 33 | 34 | private String headimgurl; 35 | 36 | private String language; 37 | 38 | private String code; 39 | 40 | //用户来源1-手动新增,2-微信公众号,3-微信小程序 41 | private Integer source; 42 | 43 | //应用类型:1-手动新增,2-微信公众号,3-微信小程序 44 | private Integer appType; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/photo/util/R.java: -------------------------------------------------------------------------------- 1 | package org.photo.util; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class R implements Serializable { 9 | 10 | private static final long serialVersionUID = -6630747483482976634L; 11 | 12 | private Integer code; 13 | private String msg; 14 | private T data; 15 | 16 | 17 | public R(Integer code, String msg, T data) { 18 | this.code = code; 19 | this.msg = msg; 20 | this.data = data; 21 | } 22 | 23 | 24 | 25 | public static R ok(T data) { 26 | return new R<>(200,"请求成功",data); 27 | } 28 | public static R no() { 29 | return new R<>(-1,"暂无数据",null); 30 | } 31 | public static R no(T data) { 32 | return new R<>(-1,"操作失败",data); 33 | } 34 | public static R data(Integer code, String msg, T data) { 35 | return new R<>(code,msg,data); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/request/HIDIdPhotoRequest.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.request; 2 | 3 | import lombok.Data; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | @Data 7 | public class HIDIdPhotoRequest { 8 | private MultipartFile input_image; // 传入的图像文件路径 9 | private String input_image_base64; // 传入的图像文件的base64编码 10 | private Integer height; // 标准证件照高度,默认为413 11 | private Integer width; // 标准证件照宽度,默认为295 12 | private String human_matting_model; // 人像分割模型,默认为modnet_photographic_portrait_matting 13 | private String face_detect_model; // 人脸检测模型,默认为mtcnn 14 | private Boolean hd; // 是否生成高清证件照,默认为true 15 | private Integer dpi; // 图像分辨率,默认为300 16 | private Boolean face_alignment; // 是否进行人脸对齐,默认为true 17 | private Float head_measure_ratio; // 面部面积与照片面积的比例,默认为0.2 18 | private Float head_height_ratio; // 面部中心与照片顶部的高度比例,默认为0.45 19 | private Float top_distance_max; // 头部与照片顶部距离的比例最大值,默认为0.12 20 | private Float top_distance_min; // 头部与照片顶部距离的比例最小值,默认为0.1 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/WebSet.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | import com.baomidou.mybatisplus.annotation.IdType; 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @TableName("web_set") 13 | public class WebSet { 14 | /** 15 | * 应用设置表 16 | */ 17 | @TableId(type = IdType.AUTO) 18 | private Integer id; 19 | /** 20 | * 小程序appid 21 | */ 22 | private String appId; 23 | /** 24 | * 小程序AppSecret 25 | */ 26 | private String appSecret; 27 | /** 28 | * 1免费下载,2视频下载 29 | */ 30 | private Integer downloadOne; 31 | /** 32 | * 1免费下载,2视频下载 33 | */ 34 | private Integer downloadTwo; 35 | /** 36 | * 是否开启鉴黄:1关闭,2开启 37 | */ 38 | private Integer safetyApi; 39 | /** 40 | * 广告位id 41 | */ 42 | private String videoUnitId; 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/vo/PhotoVo.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.vo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.util.Date; 12 | 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @TableName("photo") 17 | public class PhotoVo { 18 | /** 19 | * 图片表 20 | */ 21 | @TableId(type = IdType.AUTO) 22 | private Integer id; 23 | /** 24 | * 用户id 25 | */ 26 | private String userId; 27 | /** 28 | * 规格名字 29 | */ 30 | private String name; 31 | /** 32 | * 原图 33 | */ 34 | private String oImg; 35 | 36 | /** 37 | * 保存图 38 | */ 39 | private String nImg; 40 | /** 41 | * 尺寸 42 | */ 43 | private String size; 44 | /** 45 | * 创建时间 46 | */ 47 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 48 | private Date createTime; 49 | 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/Item.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | import com.baomidou.mybatisplus.annotation.IdType; 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @TableName("item") 13 | public class Item { 14 | @TableId(type = IdType.AUTO) 15 | private Integer id; 16 | /** 17 | * 名称 18 | */ 19 | private String name; 20 | /** 21 | * 像素-宽 22 | */ 23 | private Integer widthPx; 24 | /** 25 | * 像素-高 26 | */ 27 | private Integer heightPx; 28 | /** 29 | * 尺寸-宽 30 | */ 31 | private Integer widthMm; 32 | /** 33 | * 尺寸-高 34 | */ 35 | private Integer heightMm; 36 | /** 37 | * 图标,1-6 38 | */ 39 | private Integer icon; 40 | 41 | /** 42 | * 排序 43 | */ 44 | private Integer sort; 45 | /** 46 | * 1=常用寸照,2=各类签证,3=各类证件 47 | */ 48 | private Integer category; 49 | 50 | private Integer dpi; 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8010 3 | 4 | 5 | spring: 6 | application: 7 | name: IDPhoto 8 | profiles: 9 | #默认连接测试环境 10 | active: @spring.active@ 11 | servlet: 12 | multipart: 13 | max-file-size: 10MB 14 | max-request-size: 100MB 15 | 16 | ############## Sa-Token 配置 (文档: https://sa-token.cc) ############## 17 | sa-token: 18 | # token 名称(同时也是 cookie 名称) 19 | token-name: token 20 | # token 有效期(单位:秒) 默认30天,-1 代表永久有效 21 | timeout: 172800 22 | # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 23 | active-timeout: -1 24 | # 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录) 25 | is-concurrent: true 26 | # 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token) 27 | is-share: true 28 | # token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik) 29 | token-style: uuid 30 | # 是否输出操作日志 31 | is-log: true 32 | #一人只生成一token 33 | maxTryTimes: -1 34 | #彩色日记 35 | isColorLog: true 36 | #是否尝试从 请求体 里读取 Token 37 | isReadBody: false 38 | #是否尝试从 cookie 里读取 Token,此值为 false 后,StpUtil.login(id) 登录时也不会再往前端注入Cookie 39 | isReadCookie: false 40 | #是否在登录后将 Token 写入到响应头 41 | isWriteHeader: false 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/PhotoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 6 | import org.photo.modular.business.dao.PhotoDao; 7 | import org.photo.modular.business.model.entity.Photo; 8 | import org.photo.modular.business.service.PhotoService; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | 14 | @Service 15 | public class PhotoServiceImpl extends ServiceImpl implements PhotoService { 16 | 17 | 18 | @Override 19 | public List photoList(int pageNum, int pageSize, String userId) { 20 | Page page = new Page<>(pageNum, pageSize); 21 | QueryWrapper qw = new QueryWrapper<>(); 22 | qw.select("id","name","n_img","size","create_time"); 23 | qw.eq("user_id",userId); 24 | qw.isNotNull("n_img"); 25 | qw.orderByDesc("create_time"); 26 | Page photoPage = baseMapper.selectPage(page, qw); 27 | return photoPage.getRecords(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/contorller/CheckController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.contorller; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.photo.modular.business.model.vo.WxLoginVo; 6 | import org.photo.modular.user.vo.CustomerUserVo; 7 | import org.photo.util.R; 8 | import org.photo.util.ServletUtils; 9 | import org.photo.modular.wechat.params.LoginH5Params; 10 | import org.photo.modular.wechat.params.LoginXcxParams; 11 | import org.photo.modular.wechat.service.IWechatService; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.util.ObjectUtils; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.servlet.http.HttpServletResponse; 17 | 18 | 19 | /** 20 | * @author qb 21 | * @version 1.0 22 | * @Description 微信公众号授权及消息推送相关接口 23 | * @date 2020/9/24 11:08 24 | */ 25 | @Slf4j 26 | @RestController 27 | @RequestMapping("/") 28 | public class CheckController { 29 | 30 | 31 | /** 32 | * 微信公众号授权本地校验 33 | */ 34 | @GetMapping(value = "/MP_verify_5pwTWD7zRSautQZr.txt") 35 | public String MP_verify_5YHbaFuvibDm7jBA(HttpServletResponse response){ 36 | return "5pwTWD7zRSautQZr"; 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/Custom.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | import com.baomidou.mybatisplus.annotation.IdType; 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.Date; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @TableName("custom") 16 | public class Custom { 17 | @TableId(type = IdType.AUTO) 18 | private Integer id; 19 | /** 20 | * 用户id 21 | */ 22 | private String userId; 23 | /** 24 | * 名称 25 | */ 26 | private String name; 27 | /** 28 | * 像素-宽 29 | */ 30 | private Integer widthPx; 31 | /** 32 | * 像素-高 33 | */ 34 | private Integer heightPx; 35 | /** 36 | * 尺寸-宽 37 | */ 38 | private Integer widthMm; 39 | /** 40 | * 尺寸-高 41 | */ 42 | private Integer heightMm; 43 | 44 | @TableField(exist = false) 45 | private String size; 46 | 47 | /** 48 | * 图标,1-6 49 | */ 50 | private Integer icon; 51 | 52 | private Date createTime; 53 | 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/Photo.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | import java.util.Date; 3 | 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableLogic; 7 | import com.baomidou.mybatisplus.annotation.TableName; 8 | import com.fasterxml.jackson.annotation.JsonFormat; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @TableName("photo") 17 | public class Photo { 18 | /** 19 | * 图片表 20 | */ 21 | @TableId(type = IdType.AUTO) 22 | private Integer id; 23 | /** 24 | * 用户id 25 | */ 26 | private String userId; 27 | /** 28 | * 规格名字 29 | */ 30 | private String name; 31 | /** 32 | * 原图 33 | */ 34 | private String oImg; 35 | 36 | /** 37 | * 保存图 38 | */ 39 | private String nImg; 40 | /** 41 | * 尺寸 42 | */ 43 | private String size; 44 | /** 45 | * 创建时间 46 | */ 47 | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") 48 | private Date createTime; 49 | /** 50 | * 创建时间 51 | */ 52 | @TableLogic 53 | private Integer isDeleted; 54 | 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/response/HIDHivisionResponse.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.response; 2 | 3 | import cn.hutool.core.util.ObjectUtil; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class HIDHivisionResponse { 12 | // 状态码,true表示成功 13 | private boolean status; 14 | //标准证件照的base64编码 15 | private String imageBase64Standard; 16 | //高清证件照的base64编码 17 | private String imageBase64Hd; 18 | //其他接口返回的图片 19 | private String imageBase64; 20 | 21 | public String getImageBase64Standard() { 22 | if (ObjectUtil.isNotEmpty(imageBase64Standard) && !imageBase64Standard.startsWith("data:image")){ 23 | imageBase64Standard = "data:image/png;base64,"+imageBase64Standard; 24 | } 25 | return imageBase64Standard; 26 | } 27 | public String getImageBase64Hd() { 28 | if (ObjectUtil.isNotEmpty(imageBase64Hd) && !imageBase64Hd.startsWith("data:image")){ 29 | imageBase64Hd = "data:image/png;base64,"+imageBase64Hd; 30 | } 31 | return imageBase64Hd; 32 | } 33 | public String getImageBase64() { 34 | if (ObjectUtil.isNotEmpty(imageBase64) && !imageBase64.startsWith("data:image")){ 35 | imageBase64 = "data:image/png;base64,"+imageBase64; 36 | } 37 | return imageBase64; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/enums/HIDOptTypeEnum.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.enums; 2 | 3 | import cn.hutool.core.util.ObjectUtil; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | /** 8 | * 积分类型枚举 9 | * 10 | * @author Chopper 11 | * @since 2021/3/20 10:44 12 | */ 13 | 14 | @Getter 15 | @AllArgsConstructor 16 | public enum HIDOptTypeEnum { 17 | /** 18 | * 生成证件照(底透明) 19 | */ 20 | IDPHOTO(1, "idphoto"), 21 | /** 22 | * 添加背景色 23 | */ 24 | ADD_BACKGROUND(2, "add_background"), 25 | /** 26 | * 生成六寸排版照 27 | */ 28 | GENERATE_LAYOUT_PHOTOS(3, "generate_layout_photos"), 29 | /** 30 | * 人像抠图 31 | */ 32 | HUMAN_MATTING(4, "human_matting"), 33 | /** 34 | * 图像加水印 35 | */ 36 | WATERMARK(5, "watermark"), 37 | /** 38 | * 设置图像KB大小 39 | */ 40 | SET_KB(6, "set_kb"), 41 | /** 42 | * 证件照裁切 43 | */ 44 | IDPHOTO_CROP(7, "idphoto_crop"); 45 | 46 | 47 | private Integer value; 48 | private String name; 49 | 50 | public static String getNameByValue(Integer code){ 51 | if(ObjectUtil.isNotEmpty(code)) { 52 | for (HIDOptTypeEnum pointOptEnum : values()) { 53 | if (pointOptEnum.getValue().toString().equals(code.toString())) { 54 | return pointOptEnum.getName(); 55 | } 56 | } 57 | } 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/photo/config/CorsFilter.java: -------------------------------------------------------------------------------- 1 | package org.photo.config; 2 | 3 | import org.springframework.core.annotation.Order; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.servlet.*; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | /** 12 | * 跨域过滤器 13 | * @author click33 14 | */ 15 | @Component 16 | @Order(-200) 17 | public class CorsFilter implements Filter { 18 | 19 | static final String OPTIONS = "OPTIONS"; 20 | 21 | @Override 22 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 23 | throws IOException, ServletException { 24 | HttpServletRequest request = (HttpServletRequest) req; 25 | HttpServletResponse response = (HttpServletResponse) res; 26 | 27 | // 允许指定域访问跨域资源 28 | response.setHeader("Access-Control-Allow-Origin", "*"); 29 | // 允许所有请求方式 30 | response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); 31 | // 有效时间 32 | response.setHeader("Access-Control-Max-Age", "3600"); 33 | // 允许的header参数 34 | response.setHeader("Access-Control-Allow-Headers", "*"); 35 | 36 | // 如果是预检请求,直接返回 37 | if (OPTIONS.equals(request.getMethod())) { 38 | System.out.println("=======================浏览器发来了OPTIONS预检请求=========="); 39 | response.getWriter().print(""); 40 | return; 41 | } 42 | 43 | chain.doFilter(req, res); 44 | } 45 | 46 | @Override 47 | public void init(FilterConfig filterConfig) { 48 | } 49 | 50 | @Override 51 | public void destroy() { 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/sys/entity/SysApp.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.sys.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | /** 11 | * 应用信息实体类 12 | * 13 | * @author chenjiajun 14 | * @since 2024-02-13 15:37:46 15 | */ 16 | @Data 17 | public class SysApp implements Serializable { 18 | private static final long serialVersionUID = -35142937477811308L; 19 | 20 | 21 | @TableId(type = IdType.AUTO) 22 | /** 23 | * 主键ID 24 | */ 25 | private Integer id; 26 | 27 | /** 28 | * 拥有者名称 29 | */ 30 | private String ownerName; 31 | 32 | /** 33 | * 拥有者手机号 34 | */ 35 | private String ownerPhone; 36 | 37 | /** 38 | * 拥有者主体名称 39 | */ 40 | private String ownerCompany; 41 | 42 | /** 43 | * 拥有者主体社会信用代码 44 | */ 45 | private String ownerCompanyCode; 46 | 47 | /** 48 | * 应用名称 49 | */ 50 | private String appName; 51 | 52 | /** 53 | * 应用类型:1-手动新增,2-微信公众号,3-微信小程序 54 | */ 55 | private Integer appType; 56 | 57 | /** 58 | * 应用id 59 | */ 60 | private String appId; 61 | /** 62 | * 应用密钥 63 | */ 64 | private String appSecret; 65 | 66 | /** 67 | * 租户编号 68 | */ 69 | private String tenantCode; 70 | 71 | /** 72 | * 创建者 73 | */ 74 | private String createBy; 75 | 76 | /** 77 | * 创建时间 78 | */ 79 | private Date createTime; 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/vo/CustomerUserVo.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.vo; 2 | 3 | import com.baomidou.mybatisplus.annotation.FieldFill; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import lombok.Data; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import java.time.LocalDate; 10 | import java.time.LocalDateTime; 11 | import java.util.List; 12 | 13 | /** 14 | * 客户 15 | */ 16 | @Data 17 | public class CustomerUserVo { 18 | 19 | /**主键id*/ 20 | private String id; 21 | /**登录账号*/ 22 | private String username; 23 | /**真实姓名*/ 24 | private String realname; 25 | private String avatar; 26 | private LocalDate birthday; 27 | private Integer sex; 28 | private String email; 29 | /**1-客户,2-合作方,3-游客,4-员工*/ 30 | private Integer type; 31 | /**城市*/ 32 | private String city; 33 | /**省份*/ 34 | private String province; 35 | /**国家*/ 36 | private String country; 37 | /**语言*/ 38 | /**openid*/ 39 | private String openid; 40 | /**x小程序 openid*/ 41 | private String xcxOpenid; 42 | /**unionid*/ 43 | private String unionid; 44 | /**用户微信号*/ 45 | private String wxNum; 46 | /**微信昵称*/ 47 | private String wxNickName; 48 | /**微信头像*/ 49 | private String wxHeadImgUrl; 50 | /**是否填写手机号*/ 51 | private Boolean isBindPhone; 52 | /**是否填写实名内容*/ 53 | private Boolean isBindRealName; 54 | //头像或昵称是否为空 55 | private Integer isExtendNull; 56 | 57 | @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") 58 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 59 | private LocalDateTime createTime; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/entity/WechatUser.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.experimental.Accessors; 8 | import org.springframework.format.annotation.DateTimeFormat; 9 | 10 | import java.io.Serializable; 11 | import java.util.Date; 12 | 13 | /** 14 | * @Description: wechat_user 15 | * @Author: jeecg-boot 16 | * @Date: 2022-10-04 17 | * @Version: V1.0 18 | */ 19 | @Data 20 | @TableName("wechat_user") 21 | @Accessors(chain = true) 22 | @EqualsAndHashCode(callSuper = false) 23 | public class WechatUser implements Serializable { 24 | private static final long serialVersionUID = 1L; 25 | 26 | /**id*/ 27 | @TableId(type = IdType.ASSIGN_ID) 28 | private String id; 29 | /**关联用户ID*/ 30 | private String userId; 31 | /**openid*/ 32 | private String openid; 33 | /**unionid*/ 34 | private String unionid; 35 | /**公众号appId*/ 36 | private String appId; 37 | //应用类型:1-手动新增,2-微信公众号,3-微信小程序 38 | private Integer appType; 39 | /**创建人*/ 40 | private String createBy; 41 | /**创建日期*/ 42 | @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") 43 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 44 | @TableField(fill = FieldFill.INSERT) 45 | private Date createTime; 46 | /**更新人*/ 47 | private String updateBy; 48 | /**更新日期*/ 49 | @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") 50 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 51 | @TableField(fill = FieldFill.INSERT_UPDATE) 52 | private Date updateTime; 53 | /**所属部门*/ 54 | private String sysOrgCode; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/ItemServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 6 | import org.photo.modular.business.dao.ItemDao; 7 | import org.photo.modular.business.model.entity.Custom; 8 | import org.photo.modular.business.model.entity.Item; 9 | import org.photo.modular.business.service.CustomService; 10 | import org.photo.modular.business.service.ItemService; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | 14 | import java.util.List; 15 | 16 | 17 | @Service 18 | public class ItemServiceImpl extends ServiceImpl implements ItemService { 19 | 20 | @Autowired 21 | private CustomService customService; 22 | 23 | @Override 24 | public List itemList(int pageNum, int pageSize, int type, String userId,String name) { 25 | 26 | if(type==4){ 27 | Page page = new Page<>(pageNum, pageSize); 28 | QueryWrapper qw = new QueryWrapper<>(); 29 | qw.eq("user_id",userId); 30 | if(name!=null && !name.equals("")){ 31 | qw.like("name",name); 32 | } 33 | Page page2 = customService.page(page, qw); 34 | return (List) page2.getRecords(); 35 | } 36 | 37 | 38 | Page page = new Page<>(pageNum, pageSize); 39 | QueryWrapper qw = new QueryWrapper<>(); 40 | if(name!=null && !name.equals("")){ 41 | qw.like("name",name); 42 | }else { 43 | qw.eq("category",type); 44 | } 45 | Page page2 = baseMapper.selectPage(page, qw); 46 | return (List) page2.getRecords(); 47 | 48 | 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/photo/util/RedisUtils.java: -------------------------------------------------------------------------------- 1 | package org.photo.util; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.data.redis.core.RedisTemplate; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.util.CollectionUtils; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | @Component 11 | public class RedisUtils { 12 | 13 | @Autowired 14 | private RedisTemplate redisTemplate; 15 | 16 | /** 17 | * 指定缓存过期时间 18 | * 19 | * @param key 20 | * @param time 时间(秒) 21 | * @return 22 | */ 23 | public void expire(String key, long time){ 24 | redisTemplate.expire(key, time, TimeUnit.SECONDS); 25 | } 26 | 27 | /** 28 | * 根据key 获取过期时间 29 | * @return 时间(秒) 返回0代表为永久有效 30 | */ 31 | public long getExpire(String key){ 32 | return redisTemplate.getExpire(key); 33 | } 34 | 35 | /** 36 | * 判断key是否存在 37 | * @return 38 | */ 39 | public boolean hasKey(String key){ 40 | return redisTemplate.hasKey(key); 41 | } 42 | 43 | /** 44 | * 删除缓存 45 | * @return 46 | */ 47 | public void delete(String... key){ 48 | if(key!=null && key.length>0){ 49 | redisTemplate.delete(CollectionUtils.arrayToList(key)); 50 | } 51 | } 52 | 53 | /** 54 | * 根据key 获取缓存数据 55 | * @return 56 | */ 57 | public Object get(String key){ 58 | return redisTemplate.opsForValue().get(key); 59 | } 60 | 61 | /** 62 | * 添加缓存数据 63 | * @return 64 | */ 65 | public void set(String key, Object value){ 66 | redisTemplate.opsForValue().set(key, value); 67 | } 68 | 69 | /** 70 | * 添加缓存数据并设置过期时间 71 | * 72 | * @param time 时间小于等于0,将设置无限期 73 | * @return 74 | */ 75 | public void set(String key, Object value, long time){ 76 | redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/FilePartDetail.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.util.Date; 8 | import lombok.Data; 9 | 10 | /** 11 | * 文件分片信息表,仅在手动分片上传时使用 12 | */ 13 | @Data 14 | @TableName(value = "file_part_detail") 15 | public class FilePartDetail { 16 | /** 17 | * 分片id 18 | */ 19 | @TableId(value = "id", type = IdType.ASSIGN_ID) 20 | private String id; 21 | 22 | /** 23 | * 存储平台 24 | */ 25 | @TableField(value = "platform") 26 | private String platform; 27 | 28 | /** 29 | * 上传ID,仅在手动分片上传时使用 30 | */ 31 | @TableField(value = "upload_id") 32 | private String uploadId; 33 | 34 | /** 35 | * 分片 ETag 36 | */ 37 | @TableField(value = "e_tag") 38 | private String eTag; 39 | 40 | /** 41 | * 分片号。每一个上传的分片都有一个分片号,一般情况下取值范围是1~10000 42 | */ 43 | @TableField(value = "part_number") 44 | private Integer partNumber; 45 | 46 | /** 47 | * 文件大小,单位字节 48 | */ 49 | @TableField(value = "part_size") 50 | private Long partSize; 51 | 52 | /** 53 | * 哈希信息 54 | */ 55 | @TableField(value = "hash_info") 56 | private String hashInfo; 57 | 58 | /** 59 | * 创建时间 60 | */ 61 | @TableField(value = "create_time") 62 | private Date createTime; 63 | 64 | public static final String COL_ID = "id"; 65 | 66 | public static final String COL_PLATFORM = "platform"; 67 | 68 | public static final String COL_UPLOAD_ID = "upload_id"; 69 | 70 | public static final String COL_E_TAG = "e_tag"; 71 | 72 | public static final String COL_PART_NUMBER = "part_number"; 73 | 74 | public static final String COL_PART_SIZE = "part_size"; 75 | 76 | public static final String COL_HASH_INFO = "hash_info"; 77 | 78 | public static final String COL_CREATE_TIME = "create_time"; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/FilePartDetailService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import lombok.SneakyThrows; 8 | import org.dromara.x.file.storage.core.upload.FilePartInfo; 9 | import org.springframework.stereotype.Service; 10 | import org.photo.modular.business.dao.FilePartDetailMapper; 11 | import org.photo.modular.business.model.entity.FilePartDetail; 12 | 13 | /** 14 | * 用来将文件分片上传记录保存到数据库,这里使用了 MyBatis-Plus 和 Hutool 工具类 15 | * 目前仅手动分片分片上传时使用 16 | */ 17 | @Service 18 | public class FilePartDetailService extends ServiceImpl { 19 | 20 | private final ObjectMapper objectMapper = new ObjectMapper(); 21 | 22 | /** 23 | * 保存文件分片信息 24 | * @param info 文件分片信息 25 | */ 26 | @SneakyThrows 27 | public void saveFilePart(FilePartInfo info) { 28 | FilePartDetail detail = toFilePartDetail(info); 29 | if (save(detail)) { 30 | info.setId(detail.getId()); 31 | } 32 | } 33 | 34 | /** 35 | * 删除文件分片信息 36 | */ 37 | public void deleteFilePartByUploadId(String uploadId) { 38 | remove(new QueryWrapper().eq(FilePartDetail.COL_UPLOAD_ID, uploadId)); 39 | } 40 | 41 | /** 42 | * 将 FilePartInfo 转成 FilePartDetail 43 | * @param info 文件分片信息 44 | */ 45 | public FilePartDetail toFilePartDetail(FilePartInfo info) throws JsonProcessingException { 46 | FilePartDetail detail = new FilePartDetail(); 47 | detail.setPlatform(info.getPlatform()); 48 | detail.setUploadId(info.getUploadId()); 49 | detail.setETag(info.getETag()); 50 | detail.setPartNumber(info.getPartNumber()); 51 | detail.setPartSize(info.getPartSize()); 52 | detail.setHashInfo(valueToJson(info.getHashInfo())); 53 | detail.setCreateTime(info.getCreateTime()); 54 | return detail; 55 | } 56 | 57 | /** 58 | * 将指定值转换成 json 字符串 59 | */ 60 | public String valueToJson(Object value) throws JsonProcessingException { 61 | if (value == null) return null; 62 | return objectMapper.writeValueAsString(value); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | #默认连接测试环境 4 | active: @spring.active@ 5 | datasource: 6 | driver-class-name: com.mysql.cj.jdbc.Driver 7 | url: jdbc:mysql://127.0.0.1:3306/idphoto?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai 8 | username: root 9 | password: root 10 | hikari: 11 | minimum-idle: 3 12 | maximum-pool-size: 10 13 | max-lifetime: 30000 #不能小于30秒,否则默认回到1800秒 14 | idle-timeout: 20000 15 | connection-test-query: SELECT 1 16 | initialization-fail-timeout: 0 17 | # redis配置 18 | redis: 19 | # Redis数据库索引 20 | database: 3 21 | # Redis服务器地址 22 | host: 127.0.0.1 23 | # Redis服务器连接端口 24 | port: 6379 25 | # Redis服务器连接密码(默认为空) 26 | password: 123456 27 | # 连接超时时间 28 | timeout: 10s 29 | jedis: 30 | pool: 31 | # 连接池中的最小空闲连接 32 | min-idle: 0 33 | # 连接池中的最大空闲连接 34 | max-idle: 8 35 | # 连接池的最大数据库连接数 36 | max-active: 1000 37 | # #连接池最大阻塞等待时间(使用负值表示没有限制) 38 | max-wait: -1ms 39 | 40 | mybatis-plus: 41 | mapper-locations: classpath:mapper/*.xml 42 | configuration: 43 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 44 | auto-mapping-behavior: FULL 45 | 46 | 47 | #配置 48 | webset: 49 | #鉴黄API地址 50 | safetyDomain: http://121.62.63.137:3006/ 51 | #证件照APi地址 52 | zjzDomain: ????? 53 | 54 | dromara: 55 | x-file-storage: #文件存储配置 56 | default-platform: local-plus-1 #默认使用的存储平台 57 | thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】 58 | #对应平台的配置写在这里,注意缩进要对齐 59 | tencent-cos: 60 | - platform: tencent-cos-1 # 存储平台标识 61 | enable-storage: true # 启用存储 62 | secret-id: ????? 63 | secret-key: ????? 64 | region: ap-guangzhou #存仓库所在地域 65 | bucket-name: ????? #桶名称 66 | domain: ????? # 访问域名,注意“/”结尾,例如:https://abc.cos.ap-nanjing.myqcloud.com/ 67 | base-path: test/ # 基础路径 68 | local-plus: 69 | - platform: local-plus-1 # 存储平台标识 70 | enable-storage: true #启用存储 71 | enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高) 72 | domain: http://127.0.0.1:8081/file/preview/ # 访问域名,例如:“http://127.0.0.1:8030/file/”,注意后面要和 path-patterns 保持一致,“/”结尾,本地存储建议使用相对路径,方便后期更换域名 73 | base-path: local-plus/ # 基础路径 74 | path-patterns: /file/** # 访问路径 75 | storage-path: D:/Temp/ # 存储路径 76 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/file/ImageUpload.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.file; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.util.StringUtils; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.springframework.web.multipart.MultipartFile; 10 | import org.photo.modular.business.model.entity.PhotoRecord; 11 | import org.photo.modular.business.model.entity.WebSet; 12 | import org.photo.modular.business.service.PhotoRecordService; 13 | import org.photo.modular.business.service.UploadService; 14 | import org.photo.modular.business.service.WebSetService; 15 | import org.photo.util.R; 16 | 17 | import java.util.Date; 18 | 19 | @RestController 20 | public class ImageUpload { 21 | 22 | @Autowired 23 | private UploadService uploadService; 24 | @Autowired 25 | private WebSetService webSetService; 26 | @Autowired 27 | private PhotoRecordService photoRecordService; 28 | 29 | //图片检查 30 | @PostMapping("/upload") 31 | public R uploadImage(@RequestParam("file") MultipartFile file) { 32 | if (file.isEmpty()) { 33 | return R.no("图片不能为空"); 34 | } 35 | 36 | // 检查文件类型 37 | String originalFilename = file.getOriginalFilename(); 38 | if (!StringUtils.hasText(originalFilename) || (!originalFilename.toLowerCase().endsWith(".png") 39 | && !originalFilename.toLowerCase().endsWith(".jpg")) && !originalFilename.toLowerCase().endsWith(".jpeg")) { 40 | // 文件类型不合法 41 | return R.no("图片类型不合法,仅支持jpg/png/jpeg的图片"); 42 | } 43 | 44 | // 检查文件大小,因为现在的手机,一拍照就10多M 45 | if (file.getSize() > 15 * 1024 * 1024) { 46 | return R.no("图片大小不能超过20M"); 47 | } 48 | 49 | WebSet webSet = webSetService.getById(1); 50 | //如果开启鉴黄 51 | if(webSet.getSafetyApi()==2){ 52 | String s = uploadService.checkNsfw(file); 53 | if(s!=null){ 54 | return R.no(s); 55 | } 56 | } 57 | 58 | PhotoRecord photoRecord = new PhotoRecord(); 59 | photoRecord.setName("上传图片"); 60 | photoRecord.setUserId(StpUtil.getTokenInfo().getLoginId().toString()); 61 | photoRecord.setCreateTime(new Date()); 62 | photoRecordService.save(photoRecord); 63 | 64 | 65 | return uploadService.uploadPhoto(file,originalFilename); 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/photo/config/MyMetaObjectHandler.java: -------------------------------------------------------------------------------- 1 | package org.photo.config; 2 | 3 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 4 | import org.apache.ibatis.reflection.MetaObject; 5 | import org.photo.util.StringUtils; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.Date; 10 | 11 | /** 12 | * @program: our-task 13 | * @description: 对数据库每条记录的创建时间和更新时间自动进行填充 14 | * @author: water76016 15 | * @create: 2020-11-24 10:53 16 | **/ 17 | @Component 18 | public class MyMetaObjectHandler implements MetaObjectHandler { 19 | 20 | /** 21 | * 插入时的填充策略 22 | * @param metaObject 23 | */ 24 | @Override 25 | public void insertFill(MetaObject metaObject) { 26 | //TODO(此处没生效) 27 | if (StringUtils.isNotEmpty(metaObject.findProperty("createTime",false)) 28 | && "java.time.LocalDateTime".equals(metaObject.getGetterType("createTime").getName())){ 29 | this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); 30 | } 31 | if (StringUtils.isNotEmpty(metaObject.findProperty("updateTime",false)) 32 | && "java.time.LocalDateTime".equals(metaObject.getGetterType("updateTime").getName())){ 33 | this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); 34 | } 35 | if (StringUtils.isNotEmpty(metaObject.findProperty("createTime",false)) 36 | && "java.util.Date".equals(metaObject.getGetterType("createTime").getName())){ 37 | this.setFieldValByName("createTime", new Date(), metaObject); 38 | } 39 | if (StringUtils.isNotEmpty(metaObject.findProperty("updateTime",false)) 40 | && "java.util.Date".equals(metaObject.getGetterType("updateTime").getName())){ 41 | this.setFieldValByName("updateTime", new Date(), metaObject); 42 | } 43 | } 44 | 45 | /** 46 | * 更新时的填充策略 47 | * @param metaObject 48 | */ 49 | @Override 50 | public void updateFill(MetaObject metaObject) { 51 | if (StringUtils.isNotEmpty(metaObject.findProperty("updateTime",false)) 52 | && "java.time.LocalDateTime".equals(metaObject.getGetterType("updateTime").getName())){ 53 | this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); 54 | } 55 | if (StringUtils.isNotEmpty(metaObject.findProperty("updateTime",false)) 56 | && "java.util.Date".equals(metaObject.getGetterType("updateTime").getName())){ 57 | this.setFieldValByName("updateTime", new Date(), metaObject); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/photo/config/SaTokenConfigure.java: -------------------------------------------------------------------------------- 1 | package org.photo.config; 2 | 3 | import cn.dev33.satoken.context.SaHolder; 4 | import cn.dev33.satoken.filter.SaServletFilter; 5 | import cn.dev33.satoken.router.SaRouter; 6 | import cn.dev33.satoken.stp.StpUtil; 7 | import cn.dev33.satoken.util.SaResult; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * [Sa-Token 权限认证] 配置类 13 | */ 14 | @Configuration 15 | public class SaTokenConfigure { 16 | 17 | /** 18 | * 注册 [Sa-Token全局过滤器] 19 | */ 20 | @Bean 21 | public SaServletFilter getSaServletFilter() { 22 | return new SaServletFilter() 23 | 24 | // 指定 拦截路由 与 放行路由 25 | .addInclude("/**").addExclude("/favicon.ico") /* 排除掉 /favicon.ico */ 26 | 27 | // 认证函数: 每次请求执行 28 | .setAuth(obj -> { 29 | System.out.println("---------- 进入Sa-Token全局认证 -----------"); 30 | 31 | // 登录认证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录 32 | SaRouter.match("/**").notMatch("/wechat/**" 33 | ,"/file/preview" 34 | ,"/MP_verify_5pwTWD7zRSautQZr.txt" 35 | ,"/idPhotoApi/**" 36 | ).check(r -> StpUtil.checkLogin()); 37 | 38 | // 更多拦截处理方式,请参考“路由拦截式鉴权”章节 */ 39 | }) 40 | 41 | // 异常处理函数:每次认证函数发生异常时执行此函数 42 | .setError(e -> { 43 | System.out.println("---------- 进入Sa-Token异常处理 -----------"); 44 | System.out.println(e); 45 | System.out.println(StpUtil.getTokenValue()); 46 | return new SaResult(401, "未登录",1); 47 | }) 48 | 49 | // 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入) 50 | .setBeforeAuth(r -> { 51 | // ---------- 设置一些安全响应头 ---------- 52 | SaHolder.getResponse() 53 | // 服务器名称 54 | .setServer("IDPhotoWechat") 55 | // 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以 56 | .setHeader("X-Frame-Options", "SAMEORIGIN") 57 | // 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面 58 | .setHeader("X-XSS-Protection", "1; mode=block") 59 | // 禁用浏览器内容嗅探 60 | .setHeader("X-Content-Type-Options", "nosniff") 61 | ; 62 | }) 63 | ; 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/controller/ItemController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.controller; 2 | import cn.dev33.satoken.stp.StpUtil; 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import org.springframework.web.bind.annotation.*; 5 | import org.photo.modular.business.model.entity.Custom; 6 | import org.photo.modular.business.model.entity.Photo; 7 | import org.photo.modular.business.service.CustomService; 8 | import org.photo.modular.business.service.ItemService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.photo.modular.business.service.PhotoService; 11 | import org.photo.util.R; 12 | 13 | import java.util.Date; 14 | import java.util.List; 15 | import java.util.Random; 16 | 17 | @RestController 18 | @RequestMapping("/item") 19 | public class ItemController { 20 | 21 | 22 | @Autowired 23 | private ItemService itemService; 24 | @Autowired 25 | private CustomService customService; 26 | @Autowired 27 | private PhotoService photoService; 28 | 29 | 30 | //保存用户自定义 31 | @PostMapping("/saveCustom") 32 | public R saveCustom(@RequestBody Custom custom){ 33 | custom.setUserId(StpUtil.getTokenInfo().getLoginId().toString()); 34 | //拿mm尺寸zx 35 | String[] split = custom.getSize().split("\\*"); 36 | Random random = new Random(); 37 | custom.setIcon(random.nextInt(6) + 1); 38 | custom.setWidthMm(Integer.parseInt(split[0])); 39 | custom.setHeightMm(Integer.parseInt(split[1])); 40 | custom.setCreateTime(new Date()); 41 | customService.save(custom); 42 | return R.ok(null); 43 | } 44 | 45 | //证件列表 46 | @GetMapping("/itemList") 47 | public R itemList(int pageNum, int pageSize, int type,String name){ 48 | String userId = "0"; 49 | if(type==4){ 50 | userId = StpUtil.getTokenInfo().getLoginId().toString(); 51 | } 52 | List list = itemService.itemList(pageNum, pageSize, type,userId,name); 53 | return R.ok(list); 54 | } 55 | 56 | 57 | //用户作品列表 58 | @GetMapping("/photoList") 59 | public R photoList(int pageNum, int pageSize){ 60 | List photos = photoService.photoList(pageNum, pageSize, StpUtil.getTokenInfo().getLoginId().toString()); 61 | return R.ok(photos); 62 | } 63 | 64 | //删除作品 65 | @GetMapping("/deletePhotoId") 66 | public R deletePhotoId(int id){ 67 | QueryWrapper qw = new QueryWrapper<>(); 68 | qw.eq("id",id); 69 | qw.eq("user_id",StpUtil.getTokenInfo().getLoginId()); 70 | photoService.remove(qw); 71 | return R.ok(null); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/service/HivisionIDPhotosService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.service; 2 | 3 | import org.photo.modular.HivisionIDPhotos.enums.HIDOptTypeEnum; 4 | import org.photo.modular.HivisionIDPhotos.request.HIDAddBackgroundRequest; 5 | import org.photo.modular.HivisionIDPhotos.request.HIDIdPhotoRequest; 6 | import org.photo.modular.HivisionIDPhotos.response.HIDHivisionResponse; 7 | 8 | import java.io.IOException; 9 | 10 | public interface HivisionIDPhotosService { 11 | 12 | /** 13 | * 生成证件照 14 | * 接口的逻辑是发送一张 RGB 图像,输出一张标准证件照和一张高清证件照: 15 | * 高清证件照:根据size的宽高比例制作的证件照,文件名为output_image_dir增加_hd后缀 16 | * 标准证件照:尺寸等于size,由高清证件照缩放而来,文件名为output_image_dir 17 | * 需要注意的是,生成的两张照片都是透明的(RGBA 四通道图像),要生成完整的证件照,还需要下面的添加背景色接口。 18 | * @param request 19 | * @return 20 | * @throws IOException 21 | */ 22 | HIDHivisionResponse idphoto(HIDIdPhotoRequest request) throws IOException; 23 | 24 | /** 25 | * 添加背景色 26 | * 接口的逻辑是接收一张 RGBA 图像(透明图),根据color添加背景色,合成一张 JPG 图像。 27 | * @param request 28 | * @return 29 | * @throws IOException 30 | */ 31 | HIDHivisionResponse add_background(HIDAddBackgroundRequest request) throws IOException; 32 | 33 | /** 34 | * 生成六寸排版照 35 | * 接口的逻辑是接收一张 RGB 图像(一般为添加背景色之后的证件照),根据size进行照片排布,然后生成一张六寸排版照。 36 | * @param request 37 | * @return 38 | * @throws IOException 39 | */ 40 | HIDHivisionResponse generate_layout_photos(HIDAddBackgroundRequest request) throws IOException; 41 | 42 | /** 43 | * 人像抠图 44 | * 接口的逻辑是接收一张 RGB 图像,输出一张标准抠图人像照和高清抠图人像照(无任何背景填充)。 45 | * @param request 46 | * @return 47 | * @throws IOException 48 | */ 49 | HIDHivisionResponse human_matting(HIDAddBackgroundRequest request) throws IOException; 50 | 51 | /** 52 | * 图像加水印 53 | * 接口的功能是接收一个水印文本,然后在原图上添加指定的水印。用户可以指定水印的位置、透明度和大小等属性,以便将水印无缝地融合到原图中。 54 | * @param request 55 | * @return 56 | * @throws IOException 57 | */ 58 | HIDHivisionResponse watermark(HIDAddBackgroundRequest request) throws IOException; 59 | /** 60 | * 设置图像KB大小 61 | * 接口的功能是接收一张图像和目标文件大小(以KB为单位),如果设置的KB值小于原文件,则调整压缩率;如果设置的KB值大于源文件,则通过给文件头添加信息的方式调大KB值,目标是让图像的最终大小与设置的KB值一致。 62 | * @param request 63 | * @return 64 | * @throws IOException 65 | */ 66 | HIDHivisionResponse set_kb(HIDAddBackgroundRequest request) throws IOException; 67 | 68 | /** 69 | * 证件照裁切 70 | * 接口的功能是接收一张 RBGA 图像(透明图),输出一张标准证件照和一张高清证件照。 71 | * @param request 72 | * @return 73 | * @throws IOException 74 | */ 75 | HIDHivisionResponse idphoto_crop(HIDAddBackgroundRequest request) throws IOException; 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 证件照制作、证件照换底色、小一寸、一寸、自定义尺寸等证件照生成,AI抠图切图、全开源免费!!兼容微信H5、微信小程序两套部署!! 2 | 3 | # 预览: 4 | 5 | ![输入图片说明](assets/xcx.png) 6 | 7 | # 项目介绍 8 | 9 | #

证件照伴侣-uniapp多端兼容版

10 |

我给你的,就是我想要的,我爱你的方式,就是我希望被爱的方式.

11 |

12 | 13 | [//]: # (

喜欢就点个Star吧

) 14 | 15 | 16 | 17 | **相关项目**: 18 | - 小程序前端请前往: 19 | - gitee: https://gitee.com/wmlcjj/idphoto-uniapp 20 | - github: https://github.com/wmlcjj/idphoto-uniapp 21 | 22 | 23 | - 基于此项目二开:https://github.com/no1xuan/HivisionIDPhotos-wechat-weapp 24 | - 改动点: 25 | - 1、支持本地存储、阿里云OSS、腾讯云COS等各种对象存储云平台(支持私有存储、防止防盗刷) 26 | - 2、支持微信H5、微信小程序等多端项目(其他小程序没测试过) 27 | - 3、多端统一用户体系 28 | ------ 29 | 30 | # 📦前提准备 31 | 32 | 本项目基于HivisionIDPhotos的2024.09.10更新的版本进行对接开发 33 | 34 | 理论HivisionIDPhotos不改变入参和返回,即可直接使用最新版 35 | 36 | 1. 2024.09.10的HivisionIDPhotos(以内置MTCNN+hivision_modnet模型)下载:https://wwba.lanzouq.com/it7LW29ue8yd 37 | 38 | 2. 鉴黄APi端下载:https://github.com/no1xuan/zjzNsfw 39 | 40 | 注意: 41 | 42 | 1. **鉴黄模型目前不怎么精准,建议在小程序过审时打开,其它时间关闭** 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | # 🤩功能 53 | 54 | ##### 现有功能: 55 | 56 | - 无需单独购买API 57 | - 本地0成本处理 58 | - 无限免费调用API 59 | - 支持水印 60 | - 支持自由开关鉴黄 61 | - 自带758+尺寸 62 | - 支持自定义尺寸 63 | - 支持自定义更换背景色 64 | - 支持普通下载和高清下载 65 | - 支持引导用户打开保存相册 66 | - 支持相机拍摄和相册选择 67 | - 支持本地存储、阿里云OSS、腾讯云COS等各种对象存储云平台(支持私有存储、防止防盗刷) 68 | - 支持微信H5、微信小程序、其他小程序没测试过 69 | - 无感登录 70 | 71 |
72 | 73 | ##### 排期功能列表: 74 | 75 | - 14种衣服自由换装 76 | - 更换最新HivisionIDPhotos模型 77 | - 物品自定义抠图 78 | - 黑白图片上色 79 | - 粘土风写真生成 80 | - 管理员的后台管理 81 | - 流量主 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | # 🔧部署 90 | 91 | 需要: 92 | 93 | 1. jdk1.8+mysql8.0+redis7.2.4(mysql5.7也行) 94 | 95 | 2. Mysql导入1.sql,然后打开sys_app表,把app_id,app_secret配置了,至此Mysql配置完毕 96 | ![输入图片说明](assets/1727273729517.jpg) 97 | 98 | 3. IDEA导入项目,打开application.yml,按下图进行一步步配置 99 |

100 | 101 | ##### 修改mysql和Redis: 102 | ![输入图片说明](assets/1727272391326.jpg) 103 | 104 | 105 | 106 | ##### 修改证件照服务API地址: 107 | 108 | ![输入图片说明](assets/1727272413410.jpg) 109 | 110 | 111 | 换成你的APi地址即可,然后就可以打包了 112 | 113 | 如果不想部署API,可以使用我的: 114 | 115 | 鉴黄APi:http://121.62.63.137:3006/ 116 | 117 | 证件照APi: 可以找我拿 118 | 119 | 120 | 121 | ## ⚡️注意 122 | 123 | 1. 理论上支持本地存储,但是作者还没测试,对象存储已经过测试,作者用的是腾讯cos,按需测试哈 124 | 2. 鉴黄模型目前不怎么精准,建议在小程序过审时打开,其它时间关闭 125 | 3. 部署自已鉴黄和证件照APi时,不建议开设外网,防止被抓接口后滥用,yml里面配置127.0.0.1即可本地链接,速度还快,还安全 126 | 4. 为什么不把APi地址等参数放入数据库来配置?答:频繁使用的值,不建议与Mysql频繁握手 127 | 5. 当你部署到云上(服务器)时,别忘记配置你的小程序域名(如图)

128 | 129 | 130 | 131 | ## 📧其它 132 | 133 | 您可以通过以下方式联系我: 134 | 135 | 微信:cjj113377 136 | 137 | ![输入图片说明](assets/1727274232466.jpg) 138 | 139 | ### 特别鸣谢 140 | 141 | 基于此项目二开:https://github.com/no1xuan/HivisionIDPhotos-wechat-weapp 142 | 143 | 开源AI照证件照服务:https://github.com/Zeyi-Lin/HivisionIDPhotos 144 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/contorller/CustomerUserController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.contorller; 2 | import cn.dev33.satoken.stp.StpUtil; 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 4 | import org.dromara.x.file.storage.core.FileInfo; 5 | import org.dromara.x.file.storage.core.FileStorageService; 6 | import org.photo.modular.user.entity.CustomerUser; 7 | import org.photo.modular.user.vo.CustomerUserVo; 8 | import org.springframework.beans.BeanUtils; 9 | import org.springframework.util.ObjectUtils; 10 | import org.springframework.util.StringUtils; 11 | import org.springframework.web.bind.annotation.*; 12 | import org.photo.modular.user.service.ICustomerUserService; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.photo.util.R; 15 | import org.springframework.web.multipart.MultipartFile; 16 | 17 | import javax.servlet.http.HttpServletRequest; 18 | 19 | @RestController 20 | @RequestMapping("/user") 21 | public class CustomerUserController { 22 | 23 | @Autowired 24 | private ICustomerUserService customerUserService; 25 | @Autowired 26 | private FileStorageService fileStorageService;//注入实列 27 | 28 | /** 29 | * 根据token获取用户信息 30 | * @param 31 | * @param 32 | * @return 33 | */ 34 | @GetMapping(value = "/getUserInfo") 35 | public R getUserInfo(HttpServletRequest request){ 36 | Object userId = StpUtil.getLoginId(); 37 | CustomerUserVo userInfo = new CustomerUserVo(); 38 | if (!StringUtils.isEmpty(userId)){ 39 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 40 | queryWrapper.eq(CustomerUser::getId,userId); 41 | CustomerUser user = customerUserService.getOne(queryWrapper); 42 | BeanUtils.copyProperties(user,userInfo); 43 | } 44 | if(!ObjectUtils.isEmpty(userInfo.getId())){ 45 | return R.ok(userInfo); 46 | } 47 | return R.data(-1, "获取userInfo失败!", 1); 48 | } 49 | 50 | /** 51 | * 根据token获取用户信息 52 | * @param 53 | * @param 54 | * @return 55 | */ 56 | @PostMapping(value = "/updateUserInfo") 57 | public R getUserInfo(@RequestBody CustomerUser user){ 58 | Object userId = StpUtil.getLoginId(); 59 | user.setId(userId.toString()); 60 | customerUserService.updateById(user); 61 | return R.ok("更新成功"); 62 | } 63 | 64 | 65 | /** 66 | * 上传文件用户头像 67 | */ 68 | @PostMapping("/uploadUserAvatar") 69 | public R uploadUserAvatar(MultipartFile file) { 70 | Object userId = StpUtil.getLoginId(); 71 | FileInfo info = fileStorageService.of(file) 72 | .setPath("userAvatar/") 73 | .upload(); 74 | CustomerUser user = new CustomerUser(); 75 | user.setId(userId.toString()); 76 | user.setAvatar(info.getId()); 77 | customerUserService.updateById(user); 78 | return R.ok("上传成功"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/contorller/HivisionIDPhotosController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.contorller; 2 | 3 | import org.photo.modular.HivisionIDPhotos.request.HIDAddBackgroundRequest; 4 | import org.photo.modular.HivisionIDPhotos.request.HIDIdPhotoRequest; 5 | import org.photo.modular.HivisionIDPhotos.response.HIDHivisionResponse; 6 | import org.photo.modular.HivisionIDPhotos.service.HivisionIDPhotosService; 7 | import org.photo.util.R; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | import org.springframework.web.multipart.MultipartFile; 13 | 14 | import java.io.IOException; 15 | 16 | @RestController 17 | @RequestMapping("/idPhotoApi") 18 | public class HivisionIDPhotosController { 19 | 20 | @Autowired 21 | private HivisionIDPhotosService hivisionIDPhotosService; 22 | 23 | @PostMapping("/idphoto") 24 | public HIDHivisionResponse idphoto(MultipartFile file, HIDIdPhotoRequest request) throws IOException { 25 | request.setInput_image(file); 26 | return hivisionIDPhotosService.idphoto(request); 27 | } 28 | 29 | @PostMapping("/add_background") 30 | public R add_background(MultipartFile file, HIDAddBackgroundRequest request) throws IOException { 31 | request.setInput_image(file); 32 | return R.ok(hivisionIDPhotosService.add_background(request)); 33 | } 34 | 35 | @PostMapping("/generate_layout_photos") 36 | public R generate_layout_photos(MultipartFile file, HIDAddBackgroundRequest request) throws IOException { 37 | request.setInput_image(file); 38 | return R.ok(hivisionIDPhotosService.generate_layout_photos(request)); 39 | } 40 | 41 | @PostMapping("/human_matting") 42 | public R human_matting(MultipartFile file, HIDAddBackgroundRequest request) throws IOException { 43 | request.setInput_image(file); 44 | return R.ok(hivisionIDPhotosService.human_matting(request)); 45 | } 46 | 47 | @PostMapping("/watermark") 48 | public R watermark(MultipartFile file, HIDAddBackgroundRequest request) throws IOException { 49 | request.setInput_image(file); 50 | return R.ok(hivisionIDPhotosService.watermark(request)); 51 | } 52 | 53 | @PostMapping("/set_kb") 54 | public R set_kb(MultipartFile file, HIDAddBackgroundRequest request) throws IOException { 55 | request.setInput_image(file); 56 | return R.ok(hivisionIDPhotosService.set_kb(request)); 57 | } 58 | 59 | @PostMapping("/idphoto_crop") 60 | public R idphoto_crop(MultipartFile file, HIDAddBackgroundRequest request) throws IOException { 61 | request.setInput_image(file); 62 | return R.ok(hivisionIDPhotosService.idphoto_crop(request)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/user/entity/CustomerUser.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.user.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.*; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import org.springframework.format.annotation.DateTimeFormat; 9 | 10 | import java.io.Serializable; 11 | import java.time.LocalDate; 12 | import java.time.LocalDateTime; 13 | import java.util.Date; 14 | 15 | /** 16 | * @Description: 客户 17 | * @Author: jeecg-boot 18 | * @Date: 2022-10-01 19 | * @Version: V1.0 20 | */ 21 | @Data 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | @TableName("customer_user") 25 | public class CustomerUser implements Serializable { 26 | private static final long serialVersionUID = 1L; 27 | 28 | /**主键id*/ 29 | @TableId(type = IdType.ASSIGN_ID) 30 | private String id; 31 | /**登录账号*/ 32 | private String username; 33 | /**真实姓名*/ 34 | private String realname; 35 | /**密码*/ 36 | private String password; 37 | /**md5密码盐*/ 38 | private String salt; 39 | /**头像*/ 40 | private String avatar; 41 | /**生日*/ 42 | @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd") 43 | @DateTimeFormat(pattern="yyyy-MM-dd") 44 | private Date birthday; 45 | /**性别(0-默认未知,1-男,2-女)*/ 46 | private Integer sex; 47 | /**电子邮件*/ 48 | private String email; 49 | /**电话*/ 50 | private String phone; 51 | /**冻结状态(1-正常,2-冻结)*/ 52 | private Integer status; 53 | /**1-客户,2-合作方,3-游客,4-员工*/ 54 | private Integer type; 55 | /**城市*/ 56 | private String city; 57 | /**省份*/ 58 | private String province; 59 | /**国家*/ 60 | private String country; 61 | /**语言*/ 62 | private String language; 63 | /**备注*/ 64 | private String remarks; 65 | /**用户来源1-手动新增,2-微信公众号,3-微信小程序*/ 66 | private Integer source; 67 | /**openid*/ 68 | private String openid; 69 | /**unionid*/ 70 | private String unionid; 71 | /**用户微信号*/ 72 | private String wxNum; 73 | /**添加微信号说明*/ 74 | private String wxNumRemark; 75 | /**APP openid*/ 76 | private String appOpenid; 77 | /**x小程序 openid*/ 78 | private String xcxOpenid; 79 | /**渠道*/ 80 | private String channelId; 81 | /**微信昵称*/ 82 | private String wxNickName; 83 | /**微信头像*/ 84 | private String wxHeadImgUrl; 85 | /**创建人*/ 86 | private String createBy; 87 | /**创建时间*/ 88 | @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") 89 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 90 | @TableField(fill = FieldFill.INSERT) 91 | private LocalDateTime createTime; 92 | /**更新人*/ 93 | private String updateBy; 94 | /**更新时间*/ 95 | @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") 96 | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") 97 | @TableField(fill = FieldFill.INSERT_UPDATE) 98 | private LocalDateTime updateTime; 99 | 100 | /**删除状态(0-正常,1-已删除)*/ 101 | @TableLogic 102 | private Integer isDeleted; 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/controller/ApiController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.controller; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestBody; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.photo.modular.business.dao.WebSetDao; 10 | import org.photo.modular.business.model.entity.WebSet; 11 | import org.photo.modular.business.model.dto.CreatePhotoDto; 12 | import org.photo.modular.business.model.vo.PicVo; 13 | import org.photo.modular.business.service.ApiService; 14 | import org.photo.modular.business.service.PhotoService; 15 | import org.photo.util.R; 16 | 17 | @RestController 18 | @RequestMapping("/api") 19 | public class ApiController { 20 | 21 | 22 | @Autowired 23 | private ApiService apiService; 24 | @Autowired 25 | private WebSetDao webSetDao; 26 | 27 | 28 | @PostMapping("/getWeb") 29 | public WebSet getWeb(){ 30 | return webSetDao.getWeb(); 31 | } 32 | 33 | @PostMapping("/createIdPhoto") 34 | public R createIdPhoto(@RequestBody CreatePhotoDto createPhotoDto) { 35 | createPhotoDto.setUserId(StpUtil.getTokenInfo().getLoginId().toString()); 36 | PicVo idPhoto = apiService.createIdPhoto(createPhotoDto); 37 | if(null!=idPhoto.getMsg()){ 38 | return R.no(idPhoto.getMsg()); 39 | } 40 | return R.ok(idPhoto); 41 | } 42 | 43 | /** 44 | * 更新背景 45 | * @param createPhotoDto 46 | * @return 47 | */ 48 | 49 | @PostMapping("/updateIdPhoto") 50 | public R updateIdPhoto(@RequestBody CreatePhotoDto createPhotoDto) { 51 | createPhotoDto.setUserId(StpUtil.getTokenInfo().getLoginId().toString()); 52 | PicVo idPhoto = apiService.updateIdPhoto(createPhotoDto); 53 | if(null!=idPhoto.getMsg()){ 54 | return R.no(idPhoto.getMsg()); 55 | } 56 | return R.ok(idPhoto); 57 | } 58 | 59 | @PostMapping("/addBackground") 60 | public R addBackground(@RequestBody CreatePhotoDto createPhotoDto) { 61 | createPhotoDto.setUserId(StpUtil.getTokenInfo().getLoginId().toString()); 62 | PicVo idPhoto = apiService.updateIdPhoto(createPhotoDto); 63 | if(null!=idPhoto.getMsg()){ 64 | return R.no(idPhoto.getMsg()); 65 | } 66 | return R.ok(idPhoto); 67 | } 68 | 69 | @PostMapping("/generateLayoutPhotos") 70 | public R generateLayoutPhotos(@RequestBody CreatePhotoDto createPhotoDto) { 71 | createPhotoDto.setUserId(StpUtil.getTokenInfo().getLoginId().toString()); 72 | PicVo idPhoto = apiService.updateIdPhoto(createPhotoDto); 73 | if(null!=idPhoto.getMsg()){ 74 | return R.no(idPhoto.getMsg()); 75 | } 76 | return R.ok(idPhoto); 77 | } 78 | 79 | 80 | 81 | @PostMapping("/updateUserPhonto") 82 | public R updateUserPhonto(@RequestBody CreatePhotoDto createPhotoDto){ 83 | String userId = StpUtil.getTokenInfo().getLoginId().toString(); 84 | PicVo picVo = apiService.updateUserPhonto(userId, createPhotoDto.getImage(), createPhotoDto.getPhotoId()); 85 | if(null!=picVo.getMsg()){ 86 | return R.no(picVo.getMsg()); 87 | } 88 | return R.ok(picVo); 89 | } 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/UploadServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.core.io.ByteArrayResource; 7 | import org.springframework.http.*; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.util.LinkedMultiValueMap; 10 | import org.springframework.util.MultiValueMap; 11 | import org.springframework.web.client.RestTemplate; 12 | import org.springframework.web.multipart.MultipartFile; 13 | import org.photo.modular.business.service.UploadService; 14 | import org.photo.util.R; 15 | 16 | import java.io.IOException; 17 | import java.util.Base64; 18 | 19 | @Service 20 | public class UploadServiceImpl implements UploadService { 21 | 22 | 23 | @Value("${webset.safetyDomain}") 24 | private String safetyDomain; 25 | 26 | 27 | @Override 28 | public String checkNsfw(MultipartFile multipartFile) { 29 | 30 | 31 | try { 32 | RestTemplate restTemplate = new RestTemplate(); 33 | 34 | // 构建 multipart 数据 35 | HttpHeaders headers = new HttpHeaders(); 36 | headers.setContentType(MediaType.MULTIPART_FORM_DATA); 37 | 38 | MultiValueMap body = new LinkedMultiValueMap<>(); 39 | body.add("file",new MultipartInputStreamFileResource(multipartFile)); 40 | 41 | HttpEntity> requestEntity = new HttpEntity<>(body, headers); 42 | 43 | ResponseEntity response = restTemplate.exchange( 44 | safetyDomain+"checkImg", 45 | HttpMethod.POST, 46 | requestEntity, 47 | String.class); 48 | 49 | 50 | // 解析响应的 JSON 字符串 51 | ObjectMapper objectMapper = new ObjectMapper(); 52 | JsonNode jsonNode = objectMapper.readTree(response.getBody()); 53 | String code = jsonNode.get("code").asText(); 54 | if(code.equals("0")){ 55 | return null; 56 | }else { 57 | return "图片色情,制作失败"; 58 | } 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | return "系统繁忙,请稍后再试"; 62 | } 63 | } 64 | 65 | 66 | 67 | 68 | 69 | @Override 70 | public R uploadPhoto(MultipartFile file, String originalFilename) { 71 | try { 72 | 73 | // 直接获取文件内容 74 | byte[] fileContent = file.getBytes(); 75 | 76 | // 进行Base64编码 77 | String base64Image = Base64.getEncoder().encodeToString(fileContent); 78 | 79 | // 拼接完整的Base64图片URI 80 | String imagePrefix = ""; 81 | if (originalFilename.toLowerCase().endsWith(".png")) { 82 | imagePrefix = "data:image/png;base64,"; 83 | } else if (originalFilename.toLowerCase().endsWith(".jpg") || originalFilename.toLowerCase().endsWith(".jpeg")) { 84 | imagePrefix = "data:image/jpeg;base64,"; 85 | } 86 | 87 | //进行图片鉴黄 88 | return R.ok(imagePrefix + base64Image); 89 | 90 | } catch (IOException e) { 91 | return R.no("图片识别失败,请重试"); 92 | } 93 | } 94 | 95 | 96 | 97 | private static class MultipartInputStreamFileResource extends ByteArrayResource { 98 | private final String filename; 99 | 100 | MultipartInputStreamFileResource(MultipartFile multipartFile) throws Exception { 101 | super(multipartFile.getBytes()); 102 | this.filename = multipartFile.getOriginalFilename(); 103 | } 104 | 105 | @Override 106 | public String getFilename() { 107 | return this.filename; 108 | } 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/org/photo/util/ServletUtils.java: -------------------------------------------------------------------------------- 1 | package org.photo.util; 2 | 3 | import cn.hutool.core.convert.Convert; 4 | import org.springframework.web.context.request.RequestAttributes; 5 | import org.springframework.web.context.request.RequestContextHolder; 6 | import org.springframework.web.context.request.ServletRequestAttributes; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import javax.servlet.http.HttpSession; 11 | import java.io.IOException; 12 | 13 | /** 14 | * 客户端工具类 15 | * 16 | * @author ruoyi 17 | */ 18 | public class ServletUtils 19 | { 20 | /** 21 | * 获取String参数 22 | */ 23 | public static String getParameter(String name) 24 | { 25 | return getRequest().getParameter(name); 26 | } 27 | 28 | /** 29 | * 获取String参数 30 | */ 31 | public static String getParameter(String name, String defaultValue) 32 | { 33 | return Convert.toStr(getRequest().getParameter(name), defaultValue); 34 | } 35 | 36 | /** 37 | * 获取Integer参数 38 | */ 39 | public static Integer getParameterToInt(String name) 40 | { 41 | return Convert.toInt(getRequest().getParameter(name)); 42 | } 43 | 44 | /** 45 | * 获取Integer参数 46 | */ 47 | public static Integer getParameterToInt(String name, Integer defaultValue) 48 | { 49 | return Convert.toInt(getRequest().getParameter(name), defaultValue); 50 | } 51 | 52 | /** 53 | * 获取request 54 | */ 55 | public static HttpServletRequest getRequest() 56 | { 57 | return getRequestAttributes().getRequest(); 58 | } 59 | 60 | /** 61 | * 获取response 62 | */ 63 | public static HttpServletResponse getResponse() 64 | { 65 | return getRequestAttributes().getResponse(); 66 | } 67 | 68 | /** 69 | * 获取session 70 | */ 71 | public static HttpSession getSession() 72 | { 73 | return getRequest().getSession(); 74 | } 75 | 76 | public static ServletRequestAttributes getRequestAttributes() 77 | { 78 | RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); 79 | return (ServletRequestAttributes) attributes; 80 | } 81 | 82 | /** 83 | * 将字符串渲染到客户端 84 | * 85 | * @param response 渲染对象 86 | * @param string 待渲染的字符串 87 | * @return null 88 | */ 89 | public static String renderString(HttpServletResponse response, String string) 90 | { 91 | try 92 | { 93 | response.setStatus(200); 94 | response.setContentType("application/json"); 95 | response.setCharacterEncoding("utf-8"); 96 | response.getWriter().print(string); 97 | } 98 | catch (IOException e) 99 | { 100 | e.printStackTrace(); 101 | } 102 | return null; 103 | } 104 | 105 | /** 106 | * 是否是Ajax异步请求 107 | * 108 | * @param request 109 | */ 110 | public static boolean isAjaxRequest(HttpServletRequest request) 111 | { 112 | String accept = request.getHeader("accept"); 113 | if (accept != null && accept.indexOf("application/json") != -1) 114 | { 115 | return true; 116 | } 117 | 118 | String xRequestedWith = request.getHeader("X-Requested-With"); 119 | if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) 120 | { 121 | return true; 122 | } 123 | 124 | String uri = request.getRequestURI(); 125 | if (uri.contains(".json") || uri.contains(".xml")) 126 | { 127 | return true; 128 | } 129 | 130 | String ajax = request.getParameter("__ajax"); 131 | if (ajax.contains("json") || ajax.contains("xml")) 132 | { 133 | return true; 134 | } 135 | return false; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/model/entity/FileDetail.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.model.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableName; 7 | import java.util.Date; 8 | import lombok.Data; 9 | 10 | /** 11 | * 文件记录表 12 | */ 13 | @Data 14 | @TableName(value = "file_detail") 15 | public class FileDetail { 16 | /** 17 | * 文件id 18 | */ 19 | @TableId(value = "id", type = IdType.ASSIGN_ID) 20 | private String id; 21 | 22 | /** 23 | * 文件访问地址 24 | */ 25 | @TableField(value = "url") 26 | private String url; 27 | 28 | /** 29 | * 文件大小,单位字节 30 | */ 31 | @TableField(value = "`size`") 32 | private Long size; 33 | 34 | /** 35 | * 文件名称 36 | */ 37 | @TableField(value = "filename") 38 | private String filename; 39 | 40 | /** 41 | * 原始文件名 42 | */ 43 | @TableField(value = "original_filename") 44 | private String originalFilename; 45 | 46 | /** 47 | * 基础存储路径 48 | */ 49 | @TableField(value = "base_path") 50 | private String basePath; 51 | 52 | /** 53 | * 存储路径 54 | */ 55 | @TableField(value = "`path`") 56 | private String path; 57 | 58 | /** 59 | * 文件扩展名 60 | */ 61 | @TableField(value = "ext") 62 | private String ext; 63 | 64 | /** 65 | * MIME类型 66 | */ 67 | @TableField(value = "content_type") 68 | private String contentType; 69 | 70 | /** 71 | * 存储平台 72 | */ 73 | @TableField(value = "platform") 74 | private String platform; 75 | 76 | /** 77 | * 缩略图访问路径 78 | */ 79 | @TableField(value = "th_url") 80 | private String thUrl; 81 | 82 | /** 83 | * 缩略图名称 84 | */ 85 | @TableField(value = "th_filename") 86 | private String thFilename; 87 | 88 | /** 89 | * 缩略图大小,单位字节 90 | */ 91 | @TableField(value = "th_size") 92 | private Long thSize; 93 | 94 | /** 95 | * 缩略图MIME类型 96 | */ 97 | @TableField(value = "th_content_type") 98 | private String thContentType; 99 | 100 | /** 101 | * 文件所属对象id 102 | */ 103 | @TableField(value = "object_id") 104 | private String objectId; 105 | 106 | /** 107 | * 文件所属对象类型,例如用户头像,评价图片 108 | */ 109 | @TableField(value = "object_type") 110 | private String objectType; 111 | 112 | /** 113 | * 文件元数据 114 | */ 115 | @TableField(value = "metadata") 116 | private String metadata; 117 | 118 | /** 119 | * 文件用户元数据 120 | */ 121 | @TableField(value = "user_metadata") 122 | private String userMetadata; 123 | 124 | /** 125 | * 缩略图元数据 126 | */ 127 | @TableField(value = "th_metadata") 128 | private String thMetadata; 129 | 130 | /** 131 | * 缩略图用户元数据 132 | */ 133 | @TableField(value = "th_user_metadata") 134 | private String thUserMetadata; 135 | 136 | /** 137 | * 附加属性 138 | */ 139 | @TableField(value = "attr") 140 | private String attr; 141 | 142 | /** 143 | * 哈希信息 144 | */ 145 | @TableField(value = "hash_info") 146 | private String hashInfo; 147 | 148 | /** 149 | * 上传ID,仅在手动分片上传时使用 150 | */ 151 | @TableField(value = "upload_id") 152 | private String uploadId; 153 | 154 | /** 155 | * 上传状态,仅在手动分片上传时使用,1:初始化完成,2:上传完成 156 | */ 157 | @TableField(value = "upload_status") 158 | private Integer uploadStatus; 159 | 160 | /** 161 | * 创建时间 162 | */ 163 | @TableField(value = "create_time") 164 | private Date createTime; 165 | 166 | public static final String COL_ID = "id"; 167 | 168 | public static final String COL_URL = "url"; 169 | 170 | public static final String COL_SIZE = "size"; 171 | 172 | public static final String COL_FILENAME = "filename"; 173 | 174 | public static final String COL_ORIGINAL_FILENAME = "original_filename"; 175 | 176 | public static final String COL_BASE_PATH = "base_path"; 177 | 178 | public static final String COL_PATH = "path"; 179 | 180 | public static final String COL_EXT = "ext"; 181 | 182 | public static final String COL_CONTENT_TYPE = "content_type"; 183 | 184 | public static final String COL_PLATFORM = "platform"; 185 | 186 | public static final String COL_TH_URL = "th_url"; 187 | 188 | public static final String COL_TH_FILENAME = "th_filename"; 189 | 190 | public static final String COL_TH_SIZE = "th_size"; 191 | 192 | public static final String COL_TH_CONTENT_TYPE = "th_content_type"; 193 | 194 | public static final String COL_OBJECT_ID = "object_id"; 195 | 196 | public static final String COL_OBJECT_TYPE = "object_type"; 197 | 198 | public static final String COL_METADATA = "metadata"; 199 | 200 | public static final String COL_USER_METADATA = "user_metadata"; 201 | 202 | public static final String COL_TH_METADATA = "th_metadata"; 203 | 204 | public static final String COL_TH_USER_METADATA = "th_user_metadata"; 205 | 206 | public static final String COL_ATTR = "attr"; 207 | 208 | public static final String COL_HASH_INFO = "hash_info"; 209 | 210 | public static final String COL_UPLOAD_ID = "upload_id"; 211 | 212 | public static final String COL_UPLOAD_STATUS = "upload_status"; 213 | 214 | public static final String COL_CREATE_TIME = "create_time"; 215 | } 216 | 217 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/HivisionIDPhotos/service/impl/HivisionIDPhotosServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.HivisionIDPhotos.service.impl; 2 | 3 | import cn.hutool.core.bean.BeanUtil; 4 | import cn.hutool.core.map.MapUtil; 5 | import cn.hutool.core.util.ObjectUtil; 6 | import cn.hutool.http.HttpRequest; 7 | import cn.hutool.http.HttpResponse; 8 | import cn.hutool.json.JSONUtil; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.photo.modular.HivisionIDPhotos.enums.HIDOptTypeEnum; 12 | import org.photo.modular.HivisionIDPhotos.request.HIDAddBackgroundRequest; 13 | import org.photo.modular.HivisionIDPhotos.request.HIDIdPhotoRequest; 14 | import org.photo.modular.HivisionIDPhotos.response.HIDHivisionResponse; 15 | import org.photo.modular.HivisionIDPhotos.service.HivisionIDPhotosService; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.stereotype.Service; 18 | import org.springframework.web.multipart.MultipartFile; 19 | 20 | import java.io.IOException; 21 | import java.util.Base64; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | 26 | @Service 27 | @Slf4j 28 | public class HivisionIDPhotosServiceImpl implements HivisionIDPhotosService { 29 | 30 | @Value("${webset.zjzDomain}") 31 | private String zjzDomain; 32 | 33 | public HIDHivisionResponse idphoto(HIDIdPhotoRequest request) throws IOException { 34 | return handleRequestAndSend(HIDOptTypeEnum.IDPHOTO.getName(), request); 35 | } 36 | public HIDHivisionResponse add_background(HIDAddBackgroundRequest request) throws IOException { 37 | return handleRequestAndSend(HIDOptTypeEnum.ADD_BACKGROUND.getName(), request); 38 | } 39 | 40 | @Override 41 | public HIDHivisionResponse generate_layout_photos(HIDAddBackgroundRequest request) throws IOException { 42 | return handleRequestAndSend(HIDOptTypeEnum.GENERATE_LAYOUT_PHOTOS.getName(), request); 43 | } 44 | 45 | @Override 46 | public HIDHivisionResponse human_matting(HIDAddBackgroundRequest request) throws IOException { 47 | return handleRequestAndSend(HIDOptTypeEnum.HUMAN_MATTING.getName(), request); 48 | } 49 | 50 | @Override 51 | public HIDHivisionResponse watermark(HIDAddBackgroundRequest request) throws IOException { 52 | return handleRequestAndSend(HIDOptTypeEnum.WATERMARK.getName(), request); 53 | } 54 | 55 | @Override 56 | public HIDHivisionResponse set_kb(HIDAddBackgroundRequest request) throws IOException { 57 | return handleRequestAndSend(HIDOptTypeEnum.SET_KB.getName(), request); 58 | } 59 | 60 | @Override 61 | public HIDHivisionResponse idphoto_crop(HIDAddBackgroundRequest request) throws IOException { 62 | return handleRequestAndSend(HIDOptTypeEnum.IDPHOTO_CROP.getName(), request); 63 | } 64 | 65 | private HIDHivisionResponse handleRequestAndSend(String flag, Object requestObject) throws IOException { 66 | return handleRequestAndSend(flag, requestObject, null); 67 | } 68 | private HIDHivisionResponse handleRequestAndSend(String flag, Object requestObject, Map headers) throws IOException { 69 | Map params = BeanUtil.beanToMap(requestObject); 70 | if (ObjectUtil.isEmpty(params.get("input_image")) && ObjectUtil.isEmpty(params.get("input_image_base64"))){ 71 | throw new NullPointerException("input_image和input_image_base64必须有一个不为空"); 72 | } 73 | String url = zjzDomain.endsWith("/") ? zjzDomain : zjzDomain + "/"; 74 | if (MapUtil.isEmpty(headers)){ 75 | headers = new HashMap<>(); 76 | headers.put("Content-Type", "multipart/form-data"); 77 | } 78 | if (ObjectUtil.isNotEmpty(params.get("input_image"))){ 79 | params.put("input_image_base64", multipartFileToBase64((MultipartFile)params.get("input_image"))); 80 | params.put("input_image", null); 81 | } 82 | if (ObjectUtil.isNotEmpty(params.get("input_image_base64"))){ 83 | params.put("input_image", null); 84 | } 85 | return postRequest(url+flag, params, headers); 86 | } 87 | 88 | 89 | private HIDHivisionResponse postRequest(String url, Map params, Map headers) { 90 | try { 91 | HttpRequest request = HttpRequest.post(url).form(params); 92 | if (headers != null) { 93 | for (Map.Entry entry : headers.entrySet()) { 94 | request.header(entry.getKey(), entry.getValue()); 95 | } 96 | } 97 | HttpResponse response = request.execute(); 98 | 99 | if (response.getStatus() == 200) { 100 | HIDHivisionResponse result = JSONUtil.toBean(response.body(), HIDHivisionResponse.class); 101 | return result; 102 | } else { 103 | log.error("调用Hivision接口失败: " + response.getStatus() + " " + response.body()); 104 | throw new RuntimeException("调用Hivision接口失败: " + response.getStatus() + " " + response.body()); 105 | } 106 | } catch (Exception e) { 107 | throw new RuntimeException(e); 108 | } 109 | } 110 | 111 | public static String multipartFileToBase64(MultipartFile file) throws IOException { 112 | byte[] fileBytes = file.getBytes(); 113 | String base64String = Base64.getEncoder().encodeToString(fileBytes); 114 | String pre = "data:image/jpg;base64,"; 115 | return pre + base64String; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/org/photo/util/HttpClient.java: -------------------------------------------------------------------------------- 1 | package org.photo.util; 2 | 3 | import org.apache.http.Consts; 4 | import org.apache.http.HttpEntity; 5 | import org.apache.http.NameValuePair; 6 | import org.apache.http.ParseException; 7 | import org.apache.http.client.ClientProtocolException; 8 | import org.apache.http.client.entity.UrlEncodedFormEntity; 9 | import org.apache.http.client.methods.*; 10 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 11 | import org.apache.http.conn.ssl.TrustStrategy; 12 | import org.apache.http.entity.StringEntity; 13 | import org.apache.http.impl.client.CloseableHttpClient; 14 | import org.apache.http.impl.client.HttpClients; 15 | import org.apache.http.message.BasicNameValuePair; 16 | import org.apache.http.ssl.SSLContextBuilder; 17 | import org.apache.http.util.EntityUtils; 18 | 19 | import javax.net.ssl.SSLContext; 20 | import java.io.IOException; 21 | import java.security.cert.CertificateException; 22 | import java.security.cert.X509Certificate; 23 | import java.util.HashMap; 24 | import java.util.LinkedList; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | public class HttpClient { 29 | private String url; 30 | private Map param; 31 | private int statusCode; 32 | private String content; 33 | private String xmlParam; 34 | private boolean isHttps; 35 | 36 | public boolean isHttps() { 37 | return isHttps; 38 | } 39 | 40 | public void setHttps(boolean isHttps) { 41 | this.isHttps = isHttps; 42 | } 43 | 44 | public String getXmlParam() { 45 | return xmlParam; 46 | } 47 | 48 | public void setXmlParam(String xmlParam) { 49 | this.xmlParam = xmlParam; 50 | } 51 | 52 | public HttpClient(String url, Map param) { 53 | this.url = url; 54 | this.param = param; 55 | } 56 | 57 | public HttpClient(String url) { 58 | this.url = url; 59 | } 60 | 61 | public void setParameter(Map map) { 62 | param = map; 63 | } 64 | 65 | public void addParameter(String key, String value) { 66 | if (param == null) 67 | param = new HashMap(); 68 | param.put(key, value); 69 | } 70 | 71 | public void post() throws ClientProtocolException, IOException { 72 | HttpPost http = new HttpPost(url); 73 | setEntity(http); 74 | execute(http); 75 | } 76 | 77 | public void put() throws ClientProtocolException, IOException { 78 | HttpPut http = new HttpPut(url); 79 | setEntity(http); 80 | execute(http); 81 | } 82 | 83 | public void get() throws ClientProtocolException, IOException { 84 | if (param != null) { 85 | StringBuilder url = new StringBuilder(this.url); 86 | boolean isFirst = true; 87 | for (String key : param.keySet()) { 88 | if (isFirst) { 89 | url.append("?"); 90 | }else { 91 | url.append("&"); 92 | } 93 | url.append(key).append("=").append(param.get(key)); 94 | } 95 | this.url = url.toString(); 96 | } 97 | HttpGet http = new HttpGet(url); 98 | execute(http); 99 | } 100 | 101 | /** 102 | * set http post,put param 103 | */ 104 | private void setEntity(HttpEntityEnclosingRequestBase http) { 105 | if (param != null) { 106 | List nvps = new LinkedList(); 107 | for (String key : param.keySet()) { 108 | nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数 109 | } 110 | http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数 111 | } 112 | if (xmlParam != null) { 113 | http.setEntity(new StringEntity(xmlParam, Consts.UTF_8)); 114 | } 115 | } 116 | 117 | private void execute(HttpUriRequest http) throws ClientProtocolException, 118 | IOException { 119 | CloseableHttpClient httpClient = null; 120 | try { 121 | if (isHttps) { 122 | SSLContext sslContext = new SSLContextBuilder() 123 | .loadTrustMaterial(null, new TrustStrategy() { 124 | // 信任所有 125 | @Override 126 | public boolean isTrusted(X509Certificate[] chain, 127 | String authType) 128 | throws CertificateException { 129 | return true; 130 | } 131 | }).build(); 132 | SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( 133 | sslContext); 134 | httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) 135 | .build(); 136 | } else { 137 | httpClient = HttpClients.createDefault(); 138 | } 139 | CloseableHttpResponse response = httpClient.execute(http); 140 | try { 141 | if (response != null) { 142 | if (response.getStatusLine() != null) { 143 | statusCode = response.getStatusLine().getStatusCode(); 144 | } 145 | HttpEntity entity = response.getEntity(); 146 | // 响应内容 147 | content = EntityUtils.toString(entity, Consts.UTF_8); 148 | } 149 | } finally { 150 | response.close(); 151 | } 152 | } catch (Exception e) { 153 | e.printStackTrace(); 154 | } finally { 155 | httpClient.close(); 156 | } 157 | } 158 | 159 | public int getStatusCode() { 160 | return statusCode; 161 | } 162 | 163 | public String getContent() throws ParseException, IOException { 164 | return content; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/FileDetailService.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import cn.hutool.core.bean.BeanUtil; 4 | import cn.hutool.core.lang.Dict; 5 | import cn.hutool.core.util.StrUtil; 6 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import com.fasterxml.jackson.core.JsonProcessingException; 9 | import com.fasterxml.jackson.core.type.TypeReference; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | import lombok.SneakyThrows; 12 | import org.dromara.x.file.storage.core.FileInfo; 13 | import org.dromara.x.file.storage.core.hash.HashInfo; 14 | import org.dromara.x.file.storage.core.recorder.FileRecorder; 15 | import org.dromara.x.file.storage.core.upload.FilePartInfo; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Service; 18 | import org.photo.modular.business.dao.FileDetailMapper; 19 | import org.photo.modular.business.model.entity.FileDetail; 20 | 21 | import java.util.Map; 22 | 23 | /** 24 | * 用来将文件上传记录保存到数据库,这里使用了 MyBatis-Plus 和 Hutool 工具类 25 | */ 26 | @Service 27 | public class FileDetailService extends ServiceImpl implements FileRecorder { 28 | 29 | private final ObjectMapper objectMapper = new ObjectMapper(); 30 | 31 | @Autowired 32 | private FilePartDetailService filePartDetailService; 33 | 34 | /** 35 | * 保存文件信息到数据库 36 | */ 37 | @SneakyThrows 38 | @Override 39 | public boolean save(FileInfo info) { 40 | FileDetail detail = toFileDetail(info); 41 | boolean b = save(detail); 42 | if (b) { 43 | info.setId(detail.getId()); 44 | } 45 | return b; 46 | } 47 | 48 | /** 49 | * 更新文件记录,可以根据文件 ID 或 URL 来更新文件记录, 50 | * 主要用在手动分片上传文件-完成上传,作用是更新文件信息 51 | */ 52 | @SneakyThrows 53 | @Override 54 | public void update(FileInfo info) { 55 | FileDetail detail = toFileDetail(info); 56 | QueryWrapper qw = new QueryWrapper() 57 | .eq(detail.getUrl() != null, FileDetail.COL_URL, detail.getUrl()) 58 | .eq(detail.getId() != null, FileDetail.COL_ID, detail.getId()); 59 | update(detail, qw); 60 | } 61 | 62 | /** 63 | * 根据 url 查询文件信息 64 | */ 65 | @SneakyThrows 66 | @Override 67 | public FileInfo getByUrl(String url) { 68 | return toFileInfo(getOne(new QueryWrapper().eq(FileDetail.COL_URL, url))); 69 | } 70 | 71 | /** 72 | * 根据 url 删除文件信息 73 | */ 74 | @Override 75 | public boolean delete(String url) { 76 | remove(new QueryWrapper().eq(FileDetail.COL_URL, url)); 77 | return true; 78 | } 79 | 80 | /** 81 | * 保存文件分片信息 82 | * @param filePartInfo 文件分片信息 83 | */ 84 | @Override 85 | public void saveFilePart(FilePartInfo filePartInfo) { 86 | filePartDetailService.saveFilePart(filePartInfo); 87 | } 88 | 89 | /** 90 | * 删除文件分片信息 91 | */ 92 | @Override 93 | public void deleteFilePartByUploadId(String uploadId) { 94 | filePartDetailService.deleteFilePartByUploadId(uploadId); 95 | } 96 | 97 | /** 98 | * 将 FileInfo 转为 FileDetail 99 | */ 100 | public FileDetail toFileDetail(FileInfo info) throws JsonProcessingException { 101 | FileDetail detail = BeanUtil.copyProperties( 102 | info, FileDetail.class, "metadata", "userMetadata", "thMetadata", "thUserMetadata", "attr", "hashInfo"); 103 | 104 | // 这里手动获 元数据 并转成 json 字符串,方便存储在数据库中 105 | detail.setMetadata(valueToJson(info.getMetadata())); 106 | detail.setUserMetadata(valueToJson(info.getUserMetadata())); 107 | detail.setThMetadata(valueToJson(info.getThMetadata())); 108 | detail.setThUserMetadata(valueToJson(info.getThUserMetadata())); 109 | // 这里手动获 取附加属性字典 并转成 json 字符串,方便存储在数据库中 110 | detail.setAttr(valueToJson(info.getAttr())); 111 | // 这里手动获 哈希信息 并转成 json 字符串,方便存储在数据库中 112 | detail.setHashInfo(valueToJson(info.getHashInfo())); 113 | return detail; 114 | } 115 | 116 | /** 117 | * 将 FileDetail 转为 FileInfo 118 | */ 119 | public FileInfo toFileInfo(FileDetail detail) throws JsonProcessingException { 120 | FileInfo info = BeanUtil.copyProperties( 121 | detail, FileInfo.class, "metadata", "userMetadata", "thMetadata", "thUserMetadata", "attr", "hashInfo"); 122 | 123 | // 这里手动获取数据库中的 json 字符串 并转成 元数据,方便使用 124 | info.setMetadata(jsonToMetadata(detail.getMetadata())); 125 | info.setUserMetadata(jsonToMetadata(detail.getUserMetadata())); 126 | info.setThMetadata(jsonToMetadata(detail.getThMetadata())); 127 | info.setThUserMetadata(jsonToMetadata(detail.getThUserMetadata())); 128 | // 这里手动获取数据库中的 json 字符串 并转成 附加属性字典,方便使用 129 | info.setAttr(jsonToDict(detail.getAttr())); 130 | // 这里手动获取数据库中的 json 字符串 并转成 哈希信息,方便使用 131 | info.setHashInfo(jsonToHashInfo(detail.getHashInfo())); 132 | return info; 133 | } 134 | 135 | /** 136 | * 将指定值转换成 json 字符串 137 | */ 138 | public String valueToJson(Object value) throws JsonProcessingException { 139 | if (value == null) return null; 140 | return objectMapper.writeValueAsString(value); 141 | } 142 | 143 | /** 144 | * 将 json 字符串转换成元数据对象 145 | */ 146 | public Map jsonToMetadata(String json) throws JsonProcessingException { 147 | if (StrUtil.isBlank(json)) return null; 148 | return objectMapper.readValue(json, new TypeReference>() {}); 149 | } 150 | 151 | /** 152 | * 将 json 字符串转换成字典对象 153 | */ 154 | public Dict jsonToDict(String json) throws JsonProcessingException { 155 | if (StrUtil.isBlank(json)) return null; 156 | return objectMapper.readValue(json, Dict.class); 157 | } 158 | 159 | /** 160 | * 将 json 字符串转换成哈希信息对象 161 | */ 162 | public HashInfo jsonToHashInfo(String json) throws JsonProcessingException { 163 | if (StrUtil.isBlank(json)) return null; 164 | return objectMapper.readValue(json, HashInfo.class); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.4.0 10 | 11 | 12 | 13 | org.photo 14 | IDPhotoWechat 15 | 1.0-SNAPSHOT 16 | 17 | 18 | 8 19 | 8 20 | UTF-8 21 | 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | 31 | com.baomidou 32 | mybatis-plus-boot-starter 33 | 3.5.7 34 | 35 | 36 | mysql 37 | mysql-connector-java 38 | 8.0.22 39 | 40 | 41 | org.projectlombok 42 | lombok 43 | 44 | 45 | com.alibaba 46 | fastjson 47 | 2.0.31 48 | 49 | 50 | 51 | cn.dev33 52 | sa-token-spring-boot-starter 53 | 1.39.0 54 | 55 | 56 | 57 | cn.dev33 58 | sa-token-redis-jackson 59 | 1.39.0 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-data-redis 65 | 66 | 67 | redis.clients 68 | jedis 69 | 70 | 71 | lettuce-core 72 | io.lettuce 73 | 74 | 75 | 76 | 77 | 78 | org.apache.commons 79 | commons-pool2 80 | 81 | 82 | 83 | redis.clients 84 | jedis 85 | 3.8.0 86 | 87 | 88 | 89 | cn.hutool 90 | hutool-all 91 | 5.8.1 92 | 93 | 94 | org.apache.httpcomponents 95 | httpclient 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-starter-test 100 | 101 | 102 | junit 103 | junit 104 | test 105 | 106 | 107 | 108 | org.dromara.x-file-storage 109 | x-file-storage-spring 110 | 2.2.1 111 | 112 | 113 | com.qcloud 114 | cos_api 115 | 5.6.137 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.springframework.boot 123 | spring-boot-maven-plugin 124 | 2.7.2 125 | 126 | 127 | org.photo.IDPhotoWechatApp 128 | ZIP 129 | 130 | 131 | 132 | 133 | repackage 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | dev 145 | 146 | dev 147 | 148 | 149 | true 150 | 151 | 152 | 153 | test 154 | 155 | test 156 | 157 | 158 | 159 | prod 160 | 161 | prod 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/contorller/WechatController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.contorller; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import cn.hutool.core.util.ObjectUtil; 5 | import cn.hutool.http.HttpUtil; 6 | import com.alibaba.fastjson.JSONObject; 7 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 8 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.photo.modular.business.model.vo.WxLoginVo; 11 | import org.photo.modular.sys.service.ISysAppService; 12 | import org.photo.modular.user.dto.WechatUserInfoDto; 13 | import org.photo.modular.user.entity.CustomerUser; 14 | import org.photo.modular.user.entity.WechatUser; 15 | import org.photo.modular.user.service.ICustomerUserService; 16 | import org.photo.modular.user.service.IWechatUserService; 17 | import org.photo.modular.user.vo.CustomerUserVo; 18 | import org.photo.util.R; 19 | import org.photo.util.ServletUtils; 20 | import org.photo.modular.wechat.params.LoginH5Params; 21 | import org.photo.modular.wechat.params.LoginXcxParams; 22 | import org.photo.modular.wechat.service.IWechatService; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | import org.springframework.beans.BeanUtils; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.beans.factory.annotation.Value; 28 | import org.springframework.util.ObjectUtils; 29 | import org.springframework.util.StringUtils; 30 | import org.springframework.web.bind.annotation.*; 31 | 32 | import javax.servlet.http.HttpServletRequest; 33 | import javax.servlet.http.HttpServletResponse; 34 | import java.util.Objects; 35 | 36 | 37 | /** 38 | * @author qb 39 | * @version 1.0 40 | * @Description 微信公众号授权及消息推送相关接口 41 | * @date 2020/9/24 11:08 42 | */ 43 | @Slf4j 44 | @RestController 45 | @RequestMapping("/wechat") 46 | public class WechatController { 47 | 48 | private static String REDIS_STATE = "auth"; 49 | 50 | /** 51 | * 微信网页授权失效 4001 52 | */ 53 | Integer WECHAT_WEB_AUTH_INVALID=401; 54 | // 55 | // @Autowired 56 | // private ICustomerUserService customerUserService; 57 | @Autowired 58 | private IWechatService wechatService; 59 | 60 | /** 61 | * 微信小程序登陆 62 | * @param params 用户信息 63 | */ 64 | @PostMapping("/xcx/login") 65 | public R xcxLogin(@RequestBody LoginXcxParams params){ 66 | WxLoginVo wxlogin = null; 67 | try { 68 | CustomerUserVo user = wechatService.loginXcx(params); 69 | //saToken登陆 70 | StpUtil.login(user.getId()); 71 | //封装信息返回前端 72 | wxlogin = new WxLoginVo(); 73 | wxlogin.setToken(StpUtil.getTokenInfo().getTokenValue()); 74 | wxlogin.setIsExtendNull(user.getIsExtendNull()); 75 | } catch (Exception e) { 76 | e.printStackTrace(); 77 | } 78 | return R.ok(wxlogin); 79 | } 80 | 81 | // 82 | // /** 83 | // * 微信公众号授权url 84 | // * @param appId 公众号appiId 85 | // * @param authType 授权类型,默认1-完整授权,2-静默授权(只拿到openid) 86 | // * @param jumpUrl 最终跳转的url 87 | // * @throws IOException 88 | // */ 89 | // @RequestMapping(value = "/authorize") 90 | // public void authorize(@RequestParam String appId 91 | // , @RequestParam(value = "authType",required = false) Integer authType 92 | // , @RequestParam String jumpUrl) throws IOException { 93 | // log.error(REDIRECTURI); 94 | // String uri = CODE_URI; 95 | // 96 | // //授权类型,默认1-完整授权,2-静默授权(只拿到openid) 97 | // String scope = !ObjectUtils.isEmpty(authType) && authType == 2 ? SNSAPI_BASE : SNSAPI_USERINFO; 98 | // 99 | // //拼接上跳转的url,由前端传值(一般是网址并且URLEncode作为参数,防止参数被重定向接口识别给截取了) 100 | // String redirectUri = REDIRECTURI + "?" + "appid="+ appId 101 | // + "&jumpUrl="+ URLEncoder.encode( jumpUrl,"UTF-8"); 102 | // //微信官方需要整体再URLEncode一次 103 | // redirectUri = URLEncoder.encode(redirectUri,"UTF-8"); 104 | // 105 | // String params = "appid="+ appId 106 | // +"&redirect_uri="+ redirectUri 107 | // +"&response_type=code" 108 | // +"&scope="+ scope 109 | // +"&state="+ REDIS_STATE 110 | // +"#wechat_redirect"; 111 | // //微信获取code 112 | // String redirectUrl = uri+"?"+params; 113 | // log.info("微信获取code地址"+redirectUrl); 114 | // ServletUtils.getResponse().sendRedirect(redirectUrl); 115 | // 116 | // } 117 | 118 | /** 119 | * 获取微信openid 120 | * @param code 121 | * @param state 122 | * @throws Exception 123 | */ 124 | @GetMapping(value = "/h5/login") 125 | public void h5Login(@RequestParam String code 126 | ,@RequestParam String state 127 | ,@RequestParam String appid 128 | ,@RequestParam String jumpUrl 129 | ) throws Exception { 130 | LoginH5Params params = new LoginH5Params(); 131 | params.setAppId(appid); 132 | params.setCode(code); 133 | params.setJumpUrl(jumpUrl); 134 | CustomerUserVo user = wechatService.loginH5(params); 135 | 136 | //默认返回失效代码,如果最后获取到值,则重新赋值 137 | String redirectUri = params.getJumpUrl()+ (params.getJumpUrl().contains("?") ? "&" : "?") +"state="+ WECHAT_WEB_AUTH_INVALID; 138 | //直接更新token数据 139 | if (ObjectUtil.isNotEmpty(user) && ObjectUtil.isNotEmpty(user.getId())){ 140 | StpUtil.login(user.getId()); 141 | String token = StpUtil.getTokenInfo().getTokenValue(); 142 | //赋值跳转正常 143 | redirectUri = jumpUrl + (jumpUrl.contains("?") ? "&" : "?") + "token=" +token + "&isExtendNull=" + user.getIsExtendNull(); 144 | }else { 145 | redirectUri = jumpUrl + (jumpUrl.contains("?") ? "&" : "?") + "isExtendNull=" + user.getIsExtendNull(); 146 | } 147 | log.info("重定向地址:"+ redirectUri); 148 | ServletUtils.getResponse().sendRedirect(redirectUri); 149 | } 150 | 151 | 152 | // 153 | // /** 154 | // * 检查openid和cookie是否存在 155 | // * @param 156 | // * @param 157 | // * @return 158 | // */ 159 | // @RequestMapping(value = "/checkCookieAndOpenId") 160 | // public Result checkCookieAndOpenId(){ 161 | // String wxCookie = wechatCookieUtils.getCookieStr(); 162 | // log.info("检查获取的wxCookie:"+wxCookie); 163 | // if(StrUtil.hasEmpty(wxCookie)){ 164 | // String uuidCookie = IdUtil.simpleUUID(); 165 | // Cookie cookie = new Cookie("wx-cookie", uuidCookie); 166 | // //设置cookie域名 167 | //// cookie.setDomain(COOKIE_DOMAIN); 168 | // //设置什么方法使用, / 所有方法 169 | // cookie.setPath("/"); 170 | //// cookie.setMaxAge(EXPIRES_SECOND); 171 | // ServletUtils.getResponse().setHeader("wx-cookie",cookie.getValue()); 172 | // ServletUtils.getResponse().addCookie(cookie); 173 | // log.info(" 生成的Cookie: "+cookie.getValue()); 174 | // wxCookie = cookie.getValue(); 175 | // } 176 | // String redisValue = ObjectUtils.isEmpty(redisUtil.get(wxCookie)) ? null : redisUtil.get(wxCookie).toString(); 177 | // log.error("获取redis中的缓存的openid:"+redisValue); 178 | // if(!StrUtil.hasEmpty(redisValue)){ 179 | //// redisUtil.expire(wxCookie,EXPIRES_SECOND); 180 | // return Result.OK("获取openId成功!"); 181 | // } 182 | // return Result.error(CommonConstant.WECHAT_WEB_AUTH_INVALID,"获取openId失败!"); 183 | // } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/controller/UploadController.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.controller; 2 | 3 | import cn.dev33.satoken.stp.StpUtil; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.ibatis.annotations.Param; 7 | import org.dromara.x.file.storage.core.FileInfo; 8 | import org.dromara.x.file.storage.core.FileStorageService; 9 | import org.photo.exception.BizException; 10 | import org.photo.util.R; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.util.ObjectUtils; 14 | import org.springframework.web.bind.annotation.GetMapping; 15 | import org.springframework.web.bind.annotation.PostMapping; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.multipart.MultipartFile; 19 | import org.photo.modular.business.model.entity.FileDetail; 20 | import org.photo.modular.business.service.impl.FileDetailService; 21 | 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import java.io.*; 25 | import java.net.URLEncoder; 26 | import java.nio.charset.StandardCharsets; 27 | import java.util.Arrays; 28 | 29 | @Slf4j 30 | @RestController 31 | @RequestMapping("/file") 32 | public class UploadController { 33 | 34 | @Autowired 35 | private FileStorageService fileStorageService;//注入实列 36 | @Autowired 37 | private FileDetailService fileDetailService;//注入实列 38 | 39 | /** 40 | * 上传文件 41 | */ 42 | @PostMapping("/upload") 43 | public R upload(MultipartFile file) { 44 | FileInfo info = fileStorageService.of(file).upload(); 45 | // fileDetailService.save(info); 46 | return R.ok(info); 47 | } 48 | 49 | /** 50 | * 上传文件,成功返回文件 url 51 | */ 52 | @PostMapping("/upload2") 53 | public String upload2(MultipartFile file) { 54 | FileInfo fileInfo = fileStorageService.of(file) 55 | .setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写 56 | .setSaveFilename("image.jpg") //设置保存的文件名,不需要可以不写,会随机生成 57 | .setObjectId("0") //关联对象id,为了方便管理,不需要可以不写 58 | .setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写 59 | .putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写 60 | .upload(); //将文件上传到对应地方 61 | return fileInfo == null ? "上传失败!" : fileInfo.getUrl(); 62 | } 63 | 64 | /** 65 | * 上传图片,成功返回文件信息 66 | * 图片处理使用的是 https://github.com/coobird/thumbnailator 67 | */ 68 | @PostMapping("/upload-image") 69 | public FileInfo uploadImage(MultipartFile file) { 70 | return fileStorageService.of(file) 71 | .image(img -> img.size(1000,1000)) //将图片大小调整到 1000*1000 72 | .thumbnail(th -> th.size(200,200)) //再生成一张 200*200 的缩略图 73 | .upload(); 74 | } 75 | 76 | /** 77 | * 上传文件到指定存储平台,成功返回文件信息 78 | */ 79 | @PostMapping("/upload-platform") 80 | public FileInfo uploadPlatform(MultipartFile file) { 81 | return fileStorageService.of(file) 82 | .setPlatform("aliyun-oss-1") //使用指定的存储平台 83 | .upload(); 84 | } 85 | 86 | /** 87 | * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 88 | * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 89 | */ 90 | @PostMapping("/upload-request") 91 | public FileInfo uploadPlatform(HttpServletRequest request) { 92 | return fileStorageService.of(request).upload(); 93 | } 94 | 95 | 96 | /** 97 | * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 98 | * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 99 | */ 100 | @GetMapping("/download") 101 | public void download(HttpServletRequest request, HttpServletResponse response, @Param("id") String id) throws JsonProcessingException { 102 | FileDetail fileDetail = fileDetailService.getById(id); 103 | 104 | if (fileDetail == null) { 105 | response.setStatus(HttpServletResponse.SC_NOT_FOUND); 106 | return; 107 | } 108 | 109 | FileInfo fileInfo = fileDetailService.toFileInfo(fileDetail); 110 | 111 | try (OutputStream out = response.getOutputStream()) { 112 | // 设置适当的 Content-Type,例如:image/png,确保浏览器正确处理文件类型 113 | String contentType = getMimeType(fileInfo.getFilename()); 114 | response.setContentType(contentType); 115 | 116 | String encodedFilename = URLEncoder.encode(fileInfo.getFilename(), StandardCharsets.UTF_8.toString()) 117 | .replaceAll("\\+", "%20"); // 编码文件名,处理空格等特殊字符 118 | response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\""); 119 | 120 | response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); 121 | 122 | // 下载到 OutputStream 中 123 | fileStorageService.download(fileInfo).outputStream(out); 124 | 125 | // 确保输出流冲刷数据 126 | out.flush(); 127 | } catch (IOException e) { 128 | // 可以根据具体需求改为更详细的异常处理及响应状态设置 129 | response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 130 | } 131 | } 132 | 133 | /** 134 | * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 135 | * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 136 | */ 137 | @GetMapping("/preview") 138 | public void preview(HttpServletRequest request, HttpServletResponse response, @Param("id") String id, @Param("token") String token) throws JsonProcessingException { 139 | Object loginId = StpUtil.getLoginIdByToken(token); 140 | if (ObjectUtils.isEmpty(loginId) || Arrays.asList("-1","-2","-3","-4","-5").contains(loginId.toString())){ 141 | throw new BizException("未登录"); 142 | } 143 | FileDetail fileDetail = fileDetailService.getById(id); 144 | 145 | if (fileDetail == null) { 146 | response.setStatus(HttpServletResponse.SC_NOT_FOUND); 147 | return; 148 | } 149 | 150 | FileInfo fileInfo = fileDetailService.toFileInfo(fileDetail); 151 | 152 | try (OutputStream out = response.getOutputStream()) { 153 | // 设置适当的 Content-Type,例如:image/png,确保浏览器正确处理文件类型 154 | String contentType = getMimeType(fileInfo.getFilename()); 155 | response.setContentType(contentType); 156 | 157 | String encodedFilename = URLEncoder.encode(fileInfo.getFilename(), StandardCharsets.UTF_8.toString()) 158 | .replaceAll("\\+", "%20"); // 编码文件名,处理空格等特殊字符 159 | response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\""); 160 | 161 | // 下载到 OutputStream 中 162 | fileStorageService.download(fileInfo).outputStream(out); 163 | 164 | // 确保输出流冲刷数据 165 | out.flush(); 166 | } catch (IOException e) { 167 | // 可以根据具体需求改为更详细的异常处理及响应状态设置 168 | response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 169 | } 170 | } 171 | 172 | private String getMimeType(String filename) { 173 | // 根据文件后缀获取MIME类型,可以添加更多类型 174 | if (filename.endsWith(".png")) { 175 | return MediaType.IMAGE_PNG_VALUE; 176 | } else if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) { 177 | return MediaType.IMAGE_JPEG_VALUE; 178 | } else if (filename.endsWith(".gif")) { 179 | return MediaType.IMAGE_GIF_VALUE; 180 | } else { 181 | return MediaType.APPLICATION_OCTET_STREAM_VALUE; 182 | } 183 | } 184 | 185 | /** 186 | * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 187 | * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 188 | */ 189 | @GetMapping("/downloadByUrl") 190 | public void downloadByUrl(HttpServletRequest request, HttpServletResponse response, @Param("url") String url) throws JsonProcessingException { 191 | FileInfo fileInfo = fileDetailService.getByUrl(url); 192 | // 设置响应头以强制浏览器下载文件 193 | try { 194 | String encodedFilename = URLEncoder.encode(fileInfo.getFilename(), StandardCharsets.UTF_8.toString()) 195 | .replaceAll("\\+", "%20"); 196 | 197 | response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\""); 198 | response.setContentType(MediaType.IMAGE_PNG_VALUE); 199 | 200 | // 下载到 OutputStream 中 201 | try (OutputStream out = response.getOutputStream()) { 202 | fileStorageService.download(fileInfo).outputStream(out); 203 | out.flush(); 204 | } 205 | } catch (IOException e) { 206 | throw new BizException("File download failed", e); 207 | } 208 | } 209 | 210 | /** 211 | * 直接读取 HttpServletRequest 中的文件进行上传,成功返回文件信息 212 | * 使用这种方式有些注意事项,请查看文档 基础功能-上传 章节 213 | */ 214 | @PostMapping("/getFile") 215 | public FileDetail getFile(HttpServletRequest request, @Param("id") String id) throws JsonProcessingException { 216 | FileDetail fileDetail = fileDetailService.getById(id); 217 | 218 | // FileInfo fileInfo = fileDetailService.toFileInfo(fileDetail); 219 | 220 | //通过 FileInfo 获取文件信息 221 | // RemoteFileInfo info = fileStorageService.getFile(fileInfo); 222 | // 223 | // //文件元数据 224 | // MapProxy metadata = info.getKebabCaseInsensitiveMetadata(); 225 | // //文件用户元数据 226 | // MapProxy userMetadata = info.getKebabCaseInsensitiveUserMetadata(); 227 | // 228 | // //获取原始文件信息,这里以阿里云为例 229 | // COSObject cosObject = info.getOriginalTencentCosObject(); 230 | 231 | return fileDetail; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/wechat/service/impl/WechatServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.wechat.service.impl; 2 | 3 | import cn.hutool.core.util.ObjectUtil; 4 | import cn.hutool.http.HttpUtil; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.photo.exception.BizException; 10 | import org.photo.modular.sys.entity.SysApp; 11 | import org.photo.modular.sys.service.ISysAppService; 12 | import org.photo.modular.user.dto.WechatUserInfoDto; 13 | import org.photo.modular.user.entity.CustomerUser; 14 | import org.photo.modular.user.entity.WechatUser; 15 | import org.photo.modular.user.service.ICustomerUserService; 16 | import org.photo.modular.user.service.IWechatUserService; 17 | import org.photo.modular.user.vo.CustomerUserVo; 18 | import org.photo.modular.wechat.dto.AccessTokenDto; 19 | import org.photo.modular.wechat.params.LoginH5Params; 20 | import org.photo.modular.wechat.params.LoginXcxParams; 21 | import org.photo.modular.wechat.service.IWechatService; 22 | import org.springframework.beans.BeanUtils; 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.stereotype.Service; 25 | import org.springframework.util.ObjectUtils; 26 | import org.springframework.util.StringUtils; 27 | 28 | import java.util.Objects; 29 | 30 | /** 31 | * @Description: wechat_user 32 | * @Author: jeecg-boot 33 | * @Date: 2022-10-04 34 | * @Version: V1.0 35 | */ 36 | @Slf4j 37 | @Service 38 | public class WechatServiceImpl implements IWechatService { 39 | 40 | @Autowired 41 | private ICustomerUserService customerUserService; 42 | @Autowired 43 | private IWechatUserService wechatUserService; 44 | @Autowired 45 | private ISysAppService sysAppService; 46 | 47 | //静默授权 48 | private static String SNSAPI_BASE = "snsapi_base"; 49 | //完整授权 50 | private static String SNSAPI_USERINFO = "snsapi_userinfo"; 51 | 52 | private static final String XCX_JSCODE2SESSION_URI = "https://api.weixin.qq.com/sns/jscode2session"; 53 | private static final String H5_TOKEN_URI = "https://api.weixin.qq.com/sns/oauth2/access_token"; 54 | private static final String H5_USERINFO_URI = "https://api.weixin.qq.com/sns/userinfo"; 55 | 56 | @Override 57 | public CustomerUserVo loginXcx(LoginXcxParams params) { 58 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 59 | SysApp app = sysAppService.getOne(queryWrapper.eq(SysApp::getAppId,params.getAppId())); 60 | //发起登录请求 61 | String url = XCX_JSCODE2SESSION_URI + "?appid="+app.getAppId() 62 | +"&secret="+app.getAppSecret()+"&js_code=" + params.getCode() + "&grant_type=authorization_code"; 63 | String content = HttpUtil.get(url, 10000); 64 | //格式化微信官方返回 65 | JSONObject jsonopenid = JSONObject.parseObject(content); 66 | String openid = jsonopenid.getString("openid"); 67 | String unionid = jsonopenid.getString("unionid"); 68 | WechatUserInfoDto dto = new WechatUserInfoDto(); 69 | BeanUtils.copyProperties(params, dto); 70 | dto.setOpenid(openid); 71 | dto.setUnionid(unionid); 72 | dto.setAppType(app.getAppType()); 73 | dto.setSource(3); 74 | //校验用户是否存在 75 | LambdaQueryWrapper userWrapper = new LambdaQueryWrapper() 76 | .eq(CustomerUser::getXcxOpenid, openid) 77 | .eq(CustomerUser::getStatus, 1); 78 | CustomerUser customerUser = customerUserService.getOne(userWrapper); 79 | //如果用户不存在,则新增 80 | CustomerUserVo user = new CustomerUserVo(); 81 | if (ObjectUtil.isEmpty(customerUser)){ 82 | user = saveOrUpdateCustomerUser(customerUser,dto); 83 | }else { 84 | BeanUtils.copyProperties(customerUser, user); 85 | } 86 | //判断头像或昵称是否为空 87 | if (ObjectUtil.isEmpty(user.getWxNickName()) || ObjectUtil.isEmpty(user.getAvatar())){ 88 | user.setIsExtendNull(1); 89 | } 90 | return user; 91 | } 92 | 93 | @Override 94 | public CustomerUserVo loginH5(LoginH5Params params) { 95 | log.info("authGateway获取wxCode:code={},state={},appid={},jumpUrl={}", params.getCode(), params.getState(), params.getAppId(), params.getJumpUrl()); 96 | //appid和secret应该优先去redis里获取,如果获取不到再去数据库里获取(待优化) 97 | // String secret = redisUtil.get("key:wchat:appid:"+appId); 98 | 99 | 100 | //带参get请求,把参数拼接再url后 101 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); 102 | SysApp app = sysAppService.getOne(queryWrapper.eq(SysApp::getAppId,params.getAppId())); 103 | String urlString = H5_TOKEN_URI+"?appid="+app.getAppId()+"&secret="+app.getAppSecret()+"&code="+params.getCode()+"&grant_type=authorization_code"; 104 | String result = HttpUtil.get(urlString, 10000); 105 | // String result = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200,\"refresh_token\":\"REFRESH_TOKEN\",\"openid\":\"OPENID\",\"scope\":\"snsapi_userinfo\"}"; 106 | log.info("返回的access_token对象: {}",result); 107 | AccessTokenDto accessTokenDto = JSONObject.parseObject(result, AccessTokenDto.class); 108 | if (StringUtils.isEmpty(accessTokenDto.getAccess_token())){ 109 | throw new BizException("获取AccessToken失败"); 110 | } 111 | 112 | CustomerUserVo user = new CustomerUserVo(); 113 | 114 | //校验用户是否存在 115 | LambdaQueryWrapper userWrapper = new LambdaQueryWrapper() 116 | .eq(CustomerUser::getOpenid, accessTokenDto.getOpenid()) 117 | .eq(CustomerUser::getStatus, 1); 118 | CustomerUser customerUser = customerUserService.getOne(userWrapper); 119 | if (SNSAPI_BASE.equals(accessTokenDto.getScope()) && ObjectUtil.isNotEmpty(customerUser)){ 120 | //静默授权 121 | BeanUtils.copyProperties(customerUser, user); 122 | }else if (SNSAPI_USERINFO.equals(accessTokenDto.getScope())){ 123 | //完整授权 124 | String accessToken = accessTokenDto.getAccess_token(); 125 | String openId = accessTokenDto.getOpenid(); 126 | if (!StringUtils.isEmpty(accessToken)){ 127 | String userinfoUrl = H5_USERINFO_URI + "?access_token="+accessToken+"&openid="+openId+"&lang=zh_CN"; 128 | String userInfo = HttpUtil.get(userinfoUrl); 129 | //微信默认编码ISO-8859-1,需要转换 130 | // userInfo = new String(userInfo.getBytes("ISO-8859-1"), "UTF-8"); 131 | WechatUserInfoDto dto = JSONObject.parseObject(userInfo, WechatUserInfoDto.class); 132 | dto.setAppId(app.getAppId()); 133 | dto.setAppType(2); 134 | dto.setSource(2); 135 | //查库 136 | user = saveOrUpdateCustomerUser(customerUser,dto); 137 | } 138 | } 139 | //判断头像或昵称是否为空 140 | if (ObjectUtil.isEmpty(user.getWxHeadImgUrl()) || ObjectUtil.isEmpty(user.getAvatar())){ 141 | user.setIsExtendNull(1); 142 | } 143 | return user; 144 | } 145 | 146 | 147 | /** 148 | * 如果没有则插入,有就更新 149 | * @param customerUser 150 | * @return 151 | */ 152 | private CustomerUserVo saveOrUpdateCustomerUser(CustomerUser customerUser,WechatUserInfoDto dto){ 153 | CustomerUserVo customerUserVo = null; 154 | //如果openid查不到,再用unionid查询一次 155 | if (ObjectUtil.isEmpty(customerUser) && !StringUtils.isEmpty(dto.getUnionid())){ 156 | //校验用户是否存在 157 | LambdaQueryWrapper userWrapper = new LambdaQueryWrapper() 158 | .eq(CustomerUser::getUnionid, dto.getUnionid()) 159 | .eq(CustomerUser::getStatus, 1); 160 | customerUser = customerUserService.getOne(userWrapper); 161 | } 162 | //新增入库 163 | if (ObjectUtils.isEmpty(customerUser)){ 164 | customerUser = new CustomerUser(); 165 | customerUser.setUnionid(dto.getUnionid()); 166 | customerUser.setType(1); 167 | customerUser.setSource(dto.getSource()); 168 | } 169 | if (Objects.equals(dto.getAppType(),2)){ 170 | customerUser.setOpenid(dto.getOpenid()); 171 | }else if (Objects.equals(dto.getAppType(),3)){ 172 | customerUser.setXcxOpenid(dto.getOpenid()); 173 | } 174 | customerUser.setSex(dto.getSex()); 175 | customerUser.setLanguage(dto.getLanguage()); 176 | customerUser.setCountry(dto.getCountry()); 177 | customerUser.setProvince(dto.getProvince()); 178 | customerUser.setCity(dto.getCity()); 179 | customerUser.setWxHeadImgUrl(dto.getHeadimgurl()); 180 | customerUser.setAvatar(dto.getHeadimgurl()); 181 | customerUser.setWxNickName(dto.getNickname()); 182 | Boolean isSave = customerUserService.saveOrUpdate(customerUser); 183 | //多服务号中间表 184 | if (isSave){ 185 | WechatUser wechatUser = wechatUserService.getOne(new QueryWrapper() 186 | .eq("user_id",customerUser.getId()) 187 | .eq("app_id",dto.getAppId())); 188 | if (ObjectUtils.isEmpty(wechatUser)){ 189 | wechatUser = new WechatUser(); 190 | wechatUser.setUserId(customerUser.getId()); 191 | wechatUser.setUnionid(customerUser.getUnionid()); 192 | wechatUser.setOpenid(dto.getOpenid()); 193 | wechatUser.setAppId(dto.getAppId()); 194 | wechatUser.setAppType(dto.getAppType()); 195 | wechatUserService.save(wechatUser); 196 | } 197 | } 198 | customerUserVo = new CustomerUserVo(); 199 | BeanUtils.copyProperties(customerUser,customerUserVo); 200 | return customerUserVo; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/org/photo/modular/business/service/impl/ApiServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.photo.modular.business.service.impl; 2 | 3 | import org.dromara.x.file.storage.core.FileInfo; 4 | import org.dromara.x.file.storage.core.FileStorageService; 5 | import org.photo.modular.HivisionIDPhotos.enums.HIDOptTypeEnum; 6 | import org.photo.modular.business.model.dto.CreatePhotoDto; 7 | import org.photo.modular.business.model.entity.Custom; 8 | import org.photo.modular.business.model.entity.Item; 9 | import org.photo.modular.business.model.entity.Photo; 10 | import org.photo.modular.business.model.entity.PhotoRecord; 11 | import org.photo.modular.business.model.vo.PicVo; 12 | import org.photo.modular.business.service.*; 13 | import org.photo.modular.HivisionIDPhotos.request.HIDAddBackgroundRequest; 14 | import org.photo.modular.HivisionIDPhotos.request.HIDIdPhotoRequest; 15 | import org.photo.modular.HivisionIDPhotos.response.HIDHivisionResponse; 16 | import org.photo.modular.HivisionIDPhotos.service.HivisionIDPhotosService; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.core.io.ByteArrayResource; 20 | import org.springframework.mock.web.MockMultipartFile; 21 | import org.springframework.stereotype.Service; 22 | import org.springframework.util.StringUtils; 23 | import org.springframework.web.multipart.MultipartFile; 24 | 25 | import java.io.IOException; 26 | import java.security.MessageDigest; 27 | import java.security.NoSuchAlgorithmException; 28 | import java.text.SimpleDateFormat; 29 | import java.util.Base64; 30 | import java.util.Date; 31 | import java.util.Objects; 32 | 33 | @SuppressWarnings("DataFlowIssue") 34 | @Service 35 | public class ApiServiceImpl implements ApiService { 36 | 37 | @Value("${webset.zjzDomain}") 38 | private String zjzDomain; 39 | 40 | @Autowired 41 | private CustomService customService; 42 | @Autowired 43 | private ItemService itemService; 44 | @Autowired 45 | private PhotoService photoService; 46 | @Autowired 47 | private PhotoRecordService photoRecordService; 48 | 49 | @Autowired 50 | private FileStorageService fileStorageService;//注入实列 51 | @Autowired 52 | private HivisionIDPhotosService hivisionIDPhotosService; 53 | 54 | 55 | 56 | @Override 57 | public PicVo createIdPhoto(CreatePhotoDto createPhotoDto) { 58 | PicVo picVo = new PicVo(); 59 | Item item = null; 60 | if(createPhotoDto.getType()==0){ //定制 61 | Custom custom = customService.getById(createPhotoDto.getItemId()); 62 | if(null==custom){ 63 | picVo.setMsg("规格不存在"); 64 | return picVo; 65 | } 66 | if(!custom.getUserId().equals(createPhotoDto.getUserId())){ 67 | picVo.setMsg("非法请求"); 68 | return picVo; 69 | } 70 | createPhotoDto.setHeight(custom.getHeightPx()); 71 | createPhotoDto.setWidth(custom.getWidthPx()); 72 | 73 | }else { //列表 74 | item = itemService.getById(createPhotoDto.getItemId()); 75 | if(null==item){ 76 | picVo.setMsg("规格信息不存在"); 77 | return picVo; 78 | } 79 | createPhotoDto.setHeight(item.getHeightPx()); 80 | createPhotoDto.setWidth(item.getWidthPx()); 81 | } 82 | 83 | 84 | 85 | try { 86 | HIDIdPhotoRequest request = new HIDIdPhotoRequest(); 87 | request.setInput_image_base64(createPhotoDto.getImage()); 88 | request.setHeight(createPhotoDto.getHeight()); 89 | request.setWidth(createPhotoDto.getWidth()); 90 | request.setFace_detect_model("retinaface-resnet50"); 91 | HIDHivisionResponse hivisionDto = hivisionIDPhotosService.idphoto(request); 92 | 93 | if(!hivisionDto.isStatus()){ 94 | picVo.setMsg("未检测到人脸或多人脸"); 95 | return picVo; 96 | } 97 | //保存生成记录 98 | Photo photo = new Photo(); 99 | photo.setUserId(createPhotoDto.getUserId()); 100 | if(null==item){ 101 | photo.setName("用户自定义尺寸"); 102 | }else { 103 | photo.setName(item.getName()); 104 | } 105 | photo.setSize(createPhotoDto.getWidth()+"x"+createPhotoDto.getHeight()); 106 | photo.setCreateTime(new Date()); 107 | photoService.save(photo); 108 | 109 | //保存用户行为记录 110 | PhotoRecord record = new PhotoRecord(); 111 | record.setName("生成证件照"); 112 | record.setUserId(createPhotoDto.getUserId()); 113 | record.setCreateTime(new Date()); 114 | photoRecordService.save(record); 115 | 116 | //封装前端参数 117 | picVo.setId2(photo.getId()); 118 | picVo.setOImg(hivisionDto.getImageBase64Hd()); 119 | picVo.setKImg(hivisionDto.getImageBase64Standard()); 120 | picVo.setCImg(hivisionDto.getImageBase64Standard()); 121 | return picVo; 122 | } catch (Exception e) { 123 | e.printStackTrace(); 124 | picVo.setMsg("系统繁忙,请稍后再试"); 125 | return picVo; 126 | } 127 | 128 | 129 | } 130 | 131 | @Override 132 | public PicVo updateIdPhoto(CreatePhotoDto createPhotoDto) { 133 | PicVo picVo = new PicVo(); 134 | try { 135 | HIDHivisionResponse hivisionDto = hidvisionRequestChange(createPhotoDto); 136 | if(!hivisionDto.isStatus()){ 137 | picVo.setMsg("未检测到人脸或多人脸"); 138 | return picVo; 139 | } 140 | 141 | //保存用户行为记录 142 | PhotoRecord record = new PhotoRecord(); 143 | record.setName("换背景"); 144 | record.setUserId(createPhotoDto.getUserId()); 145 | record.setCreateTime(new Date()); 146 | photoRecordService.save(record); 147 | 148 | picVo.setCImg(hivisionDto.getImageBase64()); 149 | return picVo; 150 | } catch (Exception e) { 151 | e.printStackTrace(); 152 | picVo.setMsg("系统繁忙,请稍后再试"); 153 | return picVo; 154 | } 155 | } 156 | 157 | private HIDHivisionResponse hidvisionRequestChange(CreatePhotoDto createPhotoDto) throws IOException { 158 | if (Objects.equals(createPhotoDto.getOptType(), HIDOptTypeEnum.ADD_BACKGROUND.getValue())) { 159 | HIDAddBackgroundRequest request = new HIDAddBackgroundRequest(); 160 | request.setInput_image_base64(createPhotoDto.getImage()); 161 | request.setColor(createPhotoDto.getColors()); 162 | HIDHivisionResponse hivisionDto = hivisionIDPhotosService.add_background(request); 163 | return hivisionDto; 164 | } else if (Objects.equals(createPhotoDto.getOptType(), HIDOptTypeEnum.GENERATE_LAYOUT_PHOTOS.getValue())) { 165 | HIDAddBackgroundRequest request = new HIDAddBackgroundRequest(); 166 | return hivisionIDPhotosService.generate_layout_photos(request); 167 | } else { 168 | HIDAddBackgroundRequest request = new HIDAddBackgroundRequest(); 169 | request.setInput_image_base64(createPhotoDto.getImage()); 170 | request.setColor(createPhotoDto.getColors()); 171 | HIDHivisionResponse hivisionDto = hivisionIDPhotosService.add_background(request); 172 | return hivisionDto; 173 | } 174 | } 175 | 176 | @Override 177 | public PicVo updateUserPhonto(String userid,String img,Integer photoId) { 178 | PicVo picVo = new PicVo(); 179 | //防止被当图床 180 | Photo photo = photoService.getById(photoId); 181 | if(null==photo){ 182 | picVo.setMsg("非法请求"); 183 | return picVo; 184 | } 185 | if(!photo.getUserId().equals(userid)){ 186 | picVo.setMsg("非法请求"); 187 | return picVo; 188 | } 189 | 190 | 191 | 192 | //因为图片没刚开始存库,是为了防止性能浪费,所有由前端传入 193 | // 将图片转成MultipartFile,再次检查,防止数据伪造,如:被劫持数据包上传黄色,木马什么的 194 | MultipartFile file = base64ToMultipartFile(img); 195 | 196 | 197 | // 检查文件类型 198 | String originalFilename = file.getOriginalFilename(); 199 | if (!StringUtils.hasText(originalFilename) || (!originalFilename.toLowerCase().endsWith(".png") 200 | && !originalFilename.toLowerCase().endsWith(".jpg")) && !originalFilename.toLowerCase().endsWith(".jpeg")) { 201 | // 文件类型不合法 202 | picVo.setMsg("图片类型不合法,仅支持jpg/png/jpeg的图片"); 203 | return picVo; 204 | } 205 | 206 | //不检查图片大小,因为生成的证件照可能很大 207 | // if (file.getSize() > 15 * 1024 * 1024) { 208 | // picVo.setMsg("图片大小不能超过20M"); 209 | // return picVo; 210 | // } 211 | 212 | //不开启鉴黄,实际测试生成后的存在会被误判 213 | // WebSet webSet = webSetService.getById(1); 214 | // //如果开启鉴黄 215 | // if(webSet.getSafetyApi()==2){ 216 | // String s = uploadService.checkNsfw(file); 217 | // if(s!=null){ 218 | // picVo.setMsg(s); 219 | // return picVo; 220 | // } 221 | // } 222 | 223 | FileInfo info = fileStorageService.of(file).upload(); 224 | String imagePath = info.getUrl(); 225 | String picId = info.getId(); 226 | 227 | photo.setId(photoId); 228 | photo.setNImg(picId); 229 | photoService.updateById(photo); 230 | 231 | 232 | //保存用户行为记录 233 | PhotoRecord record = new PhotoRecord(); 234 | record.setName("下载证件照"); 235 | record.setUserId(userid); 236 | record.setCreateTime(new Date()); 237 | photoRecordService.save(record); 238 | picVo.setPicUrl(imagePath); 239 | picVo.setPicId(picId); 240 | return picVo; 241 | 242 | } 243 | 244 | 245 | private static class MultipartInputStreamFileResource extends ByteArrayResource { 246 | private final String filename; 247 | 248 | MultipartInputStreamFileResource(MultipartFile multipartFile) throws Exception { 249 | super(multipartFile.getBytes()); 250 | this.filename = multipartFile.getOriginalFilename(); 251 | } 252 | 253 | @Override 254 | public String getFilename() { 255 | return this.filename; 256 | } 257 | } 258 | 259 | 260 | private static MultipartFile base64ToMultipartFile(String base64) { 261 | if (!base64.startsWith("data:image")){ 262 | base64 = "data:image/png;base64,"+base64; 263 | } 264 | // 提取Base64内容 265 | String[] baseStrs = base64.split(","); 266 | 267 | // 获取 MIME 类型 268 | String mimeType = baseStrs[0].split(":")[1].split(";")[0]; 269 | String extension = mimeType.split("/")[1]; // 提取文件扩展名 270 | 271 | // 解码Base64数据 272 | byte[] data = Base64.getDecoder().decode(baseStrs[1]); 273 | 274 | // 创建MultipartFile对象 275 | return new MockMultipartFile( 276 | "file", // 表单字段名 277 | "filename." + extension, // 原始文件名,带扩展名 278 | mimeType, // 文件类型 279 | data // 文件数据 280 | ); 281 | } 282 | 283 | 284 | 285 | private String generateUniqueFilename(String originalFilename, MultipartFile file) { 286 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); 287 | String timestamp = dateFormat.format(new Date()); 288 | String contentHash = getFileContentHash(file); // 获取文件内容哈希值 289 | return timestamp + contentHash + getExtension(originalFilename); 290 | } 291 | 292 | private String getFileContentHash(MultipartFile file) { 293 | try { 294 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); 295 | byte[] bytes = digest.digest(file.getBytes()); 296 | StringBuilder builder = new StringBuilder(); 297 | for (byte b : bytes) { 298 | builder.append(String.format("%02x", b)); 299 | } 300 | return builder.toString(); 301 | } catch (NoSuchAlgorithmException | IOException e) { 302 | e.printStackTrace(); 303 | } 304 | return ""; 305 | } 306 | 307 | private String getExtension(String filename) { 308 | int dotIndex = filename.lastIndexOf("."); 309 | if (dotIndex > 0 && dotIndex < filename.length() - 1) { 310 | return filename.substring(dotIndex); 311 | } 312 | return ""; 313 | } 314 | 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/main/java/org/photo/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package org.photo.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonSyntaxException; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.io.IOException; 8 | import java.io.UnsupportedEncodingException; 9 | import java.net.URLEncoder; 10 | import java.security.MessageDigest; 11 | import java.security.NoSuchAlgorithmException; 12 | import java.text.DateFormat; 13 | import java.text.DecimalFormat; 14 | import java.text.ParseException; 15 | import java.text.SimpleDateFormat; 16 | import java.util.*; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | /** 21 | * 字符串工具类 22 | */ 23 | public class StringUtils { 24 | 25 | private static final Pattern PATTERN_ISEMAIL = Pattern.compile("^[\\w\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{1,4}$"); 26 | 27 | private static final Pattern PATTERN_ISURL = Pattern.compile("^(http://|https://)?([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9]{1,4}(/.*)?"); 28 | 29 | private static final Pattern PATTERN_TRIM = Pattern.compile("^\\s+|\\s+$"); 30 | 31 | private static final Pattern PATTERN_TRIM_LEFT = Pattern.compile("^\\s+"); 32 | 33 | private static final Pattern PATTERN_TRIM_RIGHT = Pattern.compile("\\s+$"); 34 | 35 | private static final Pattern PATTERN_ISNUMERIC = Pattern.compile("^\\d+(\\.\\d+)?$"); 36 | 37 | private static final Pattern PATTERN_ISLOGINNAME = Pattern.compile("^[0-9a-zA-Z_@.-]+$"); 38 | 39 | private static final Pattern PATTERN_ISNAME = Pattern.compile("^([a-zA-Z\\x20]+|[\\u4e00-\\u9fa5]+)$"); 40 | 41 | private static final Pattern PATTERN_ISALPHAMERIC = Pattern.compile("^[a-zA-Z0-9]+$"); 42 | 43 | public static final String[] NULL_STRING_ARRAY = new String[0]; 44 | 45 | public static final String CRLF = System.getProperty("line.separator"); 46 | 47 | /** 48 | * 要把输入内容组合成 sql语句时, 使用该函数过滤特殊字符以防SQL注入攻击 49 | * 1. 如果期待数字型输入, 使用isNumeric方法判断是否数字 50 | * 2. 如果期待字符型输入, 只要没有输入中没有单引号, 即可保证自己的sql语句不被破坏 Java中通常使用 PreparedStatement , 51 | * SQL注入漏洞几乎可以避免 52 | */ 53 | public static String sqlParamFilter(String s) { 54 | if (s == null || s.length() < 1) { 55 | return ""; 56 | } 57 | if ("update".equalsIgnoreCase(s) || "delete".equalsIgnoreCase(s) || "insert".equalsIgnoreCase(s)) { 58 | return s; 59 | } 60 | s = s.replaceAll("'", "''"); 61 | s = s.replaceAll("--", "- -"); 62 | s = s.replaceAll("%", "%"); 63 | s = s.replaceAll("(?i)select", "select"); 64 | s = s.replaceAll("(?i)exec", "exec"); 65 | s = s.replaceAll("(?i)declare", "declare"); 66 | s = s.replaceAll("(?i)delete", "delete"); 67 | s = s.replaceAll("(?i)insert", "insert"); 68 | s = s.replaceAll("(?i)update", "update"); 69 | return s.trim(); 70 | } 71 | 72 | /** 73 | * 判断一个字符串是否是数字串 74 | */ 75 | public static boolean isNumeric(String s) { 76 | if (StringUtils.hasText(s)) { 77 | return StringUtils.PATTERN_ISNUMERIC.matcher(s).matches(); 78 | } else { 79 | return false; 80 | } 81 | } 82 | 83 | /** 84 | * 判断字符串是否只包含字母和数字 85 | */ 86 | public static boolean isAlphameric(final String s) { 87 | if (StringUtils.hasText(s)) { 88 | return StringUtils.PATTERN_ISALPHAMERIC.matcher(s).matches(); 89 | } else { 90 | return false; 91 | } 92 | } 93 | 94 | /** 95 | * 判断指定字符串是否符合登陆用户名的规则(仅包含字母数字下划线等) 96 | */ 97 | public static boolean isLoginName(final String s) { 98 | return StringUtils.PATTERN_ISLOGINNAME.matcher(s).matches(); 99 | } 100 | 101 | /** 102 | * 判断指定字符串是否符合中文姓名或者英文姓名的规则 103 | */ 104 | public static boolean isName(final String s) { 105 | return StringUtils.PATTERN_ISNAME.matcher(s).matches(); 106 | } 107 | 108 | /** 109 | * 检查一个字符串的长度(字节数)是否在指定范围之内 110 | * 111 | * @param s 字符串 112 | * @param minlen 0 表示不限制最小长度 113 | * @param maxlen 0 表示不限制最大长度 114 | * @return 在范围内返回true,否则返回false 115 | */ 116 | public static boolean checkLength(final String s, final int minlen, final int maxlen) { 117 | if (StringUtils.hasText(s)) { 118 | int len = s.getBytes().length; 119 | return len >= minlen && (len <= maxlen || maxlen <= 0); 120 | } else { 121 | return minlen <= 0; 122 | } 123 | } 124 | 125 | /** 126 | * 检查一个字符串是否非空(未去除空格) 127 | * 128 | * @param s 字符串 129 | * @return 字符串长度大于0则返回true;否则返回false 130 | */ 131 | public static boolean hasText(final String s) { 132 | if (s == null) { 133 | return false; 134 | } 135 | if (s.length() > 0) { 136 | return true; 137 | } 138 | return false; 139 | } 140 | 141 | /** 142 | * 检查一个字符串是否符合Email地址的格式 143 | * 144 | * @param s 字符串 145 | * @return 符合Email格式则返回true,否则返回false 146 | */ 147 | public static boolean isEmail(final String s) { 148 | if (s == null) { 149 | return false; 150 | } 151 | return StringUtils.PATTERN_ISEMAIL.matcher(s).matches(); 152 | } 153 | 154 | /** 155 | * 检查一个字符串是否符合URL地址格式 156 | * 157 | * @param s 字符串 158 | * @return 符合url格式则返回true;否则返回false 159 | */ 160 | public static boolean isURL(final String s) { 161 | return StringUtils.PATTERN_ISURL.matcher(s).matches(); 162 | } 163 | 164 | /** 165 | * 根据字节长度截取字符串,截掉超过 len 个字节的部分 166 | * 167 | * @param s 字符串 168 | * @param len 最大长度 169 | * @return 该字符串前len个字节的字串;字符串为null则返回空字符串 170 | */ 171 | public static String left(String s, int len) { 172 | if (s == null) return ""; 173 | int j = 0; 174 | StringBuilder sb = new StringBuilder(); 175 | for (int i = 0; i < s.length(); i++) { 176 | char c = s.charAt(i); 177 | if (c > 255) { 178 | j += 2; 179 | } else { 180 | j++; 181 | } 182 | if (j <= len) { 183 | sb.append(c); 184 | } else { 185 | break; 186 | } 187 | } 188 | return sb.toString(); 189 | } 190 | 191 | /** 192 | * 从指定位置si开始取得指定字符串中长度为len的子字符串 193 | * 194 | * @param s 字符串 195 | * @param si 开始位置(0开始) 196 | * @param len 长度 197 | * @return 子字符串 198 | */ 199 | public static String substr(final String s, final int si, final int len) { 200 | if (StringUtils.hasText(s)) { 201 | int length = s.length(); 202 | int ei = si + len; 203 | if (length >= ei) { 204 | return s.substring(si, ei); 205 | } else { 206 | return s; 207 | } 208 | } else { 209 | return ""; 210 | } 211 | } 212 | 213 | /** 214 | * 取正整数 215 | * 216 | * @param s 217 | * @return 218 | */ 219 | public static int intPositiveValue(final String s) { 220 | int retInt = StringUtils.intValue(s, 10); 221 | if (retInt < 0) { 222 | retInt = -retInt; 223 | } 224 | return retInt; 225 | } 226 | 227 | /** 228 | * 按十进制返回一个字符串所表示的整数 229 | * 230 | * @param s 字符串 231 | * @return 字符串字面值所表示的整数值,不能解释为整数则返回0 232 | */ 233 | public static int intValue(final String s) { 234 | return StringUtils.intValue(s, 10); 235 | } 236 | 237 | /** 238 | * 将字符串数组转换为整型数组,将数组中的每一个字符串分别进行转换 239 | * 240 | * @param ss 字符串数组 241 | * @return 整形数组 242 | */ 243 | public static int[] intValues(String[] ss) { 244 | if (ss == null || ss.length < 1) return new int[0]; 245 | int[] is = new int[ss.length]; 246 | for (int i = 0; i < ss.length; i++) { 247 | is[i] = intValue(ss[i]); 248 | } 249 | return is; 250 | } 251 | 252 | /** 253 | * 按指定基数(进制)返回字符串表示的整数 254 | * 255 | * @param s 字符串 256 | * @param r 基数(进制) 257 | * @return 整数;不能按指定基数解释为整数的返回0 258 | */ 259 | public static int intValue(final String s, final int r) { 260 | if (s == null) { 261 | return 0; 262 | } 263 | int i = 0; 264 | try { 265 | i = Integer.parseInt(s, r); 266 | } catch (Exception e) { 267 | i = 0; 268 | } 269 | return i; 270 | } 271 | 272 | 273 | /** 274 | * 转换为float 275 | * @param s 276 | * @return 277 | */ 278 | public static float floatValue(final String s){ 279 | if (s == null) { 280 | return 0f; 281 | } 282 | float i = 0f; 283 | try { 284 | i = Float.parseFloat(s); 285 | } catch (Exception e) { 286 | i = 0f; 287 | } 288 | return i; 289 | } 290 | 291 | 292 | public static Integer intValueNull(final String s) { 293 | if (s == null) { 294 | return null; 295 | } 296 | Integer i = null; 297 | try { 298 | i = Integer.parseInt(s, 10); 299 | } catch (Exception e) { 300 | //i = 0; 301 | e.printStackTrace(); 302 | } 303 | return i; 304 | } 305 | 306 | public static long longValue(final String s) { 307 | return StringUtils.longValue(s, 10); 308 | } 309 | 310 | public static long longValue(final String s, final int r) { 311 | if (s == null) { 312 | return 0; 313 | } 314 | long i = 0; 315 | try { 316 | i = Long.parseLong(s, r); 317 | } catch (Exception e) { 318 | i = 0; 319 | } 320 | return i; 321 | } 322 | 323 | 324 | public static String nvl(final String s) { 325 | return s == null ? "" : s.toString(); 326 | } 327 | 328 | /** 329 | * 如果一个字符串是 null,则返回 空字符串 330 | */ 331 | public static String nvl(final Object s) { 332 | return s == null ? "" : s.toString(); 333 | } 334 | 335 | 336 | public static String replaceBlank(String str) { 337 | String dest = ""; 338 | if (str != null) { 339 | Pattern p = Pattern.compile("\\s*|\t|\r|\n"); 340 | Matcher m = p.matcher(str); 341 | dest = m.replaceAll(""); 342 | } 343 | return dest; 344 | } 345 | 346 | /** 347 | * 如果一个字符串是 null ,则返回指定字符串 348 | */ 349 | public static String nvl(final String s, final String r) { 350 | return s == null ? r : s; 351 | } 352 | 353 | /** 354 | * 去除字符串的首尾空白字符 355 | */ 356 | public static String trim(final String s) { 357 | if (!StringUtils.hasText(s)) { 358 | return ""; 359 | } 360 | return StringUtils.PATTERN_TRIM.matcher(s).replaceAll(""); 361 | } 362 | 363 | /** 364 | * 去除字符串的首部空白字符 365 | */ 366 | public static String trimLeft(final String s) { 367 | if (!StringUtils.hasText(s)) { 368 | return ""; 369 | } 370 | return StringUtils.PATTERN_TRIM_LEFT.matcher(s).replaceAll(""); 371 | } 372 | 373 | /** 374 | * 去除字符串的尾部空白字符 375 | */ 376 | public static String trimRight(final String s) { 377 | if (!StringUtils.hasText(s)) { 378 | return ""; 379 | } 380 | return StringUtils.PATTERN_TRIM_RIGHT.matcher(s).replaceAll(""); 381 | } 382 | 383 | /** 384 | * 处理字符串,将其中的 html 字符(& , < , > , ")分别编码为(&amp; &lt; &gt; &quot;);回车换行符替换为空格 385 | * 386 | * @return 处理后的结果 387 | */ 388 | public static String htmlEncode(final String s) { 389 | return StringUtils.htmlEncode(s, false); 390 | } 391 | 392 | /** 393 | * 处理字符串,将其中的 html 字符(& , < , > , ")分别编码为(&amp; &lt; &gt; &quot;) 394 | * 395 | * @param s 字符串 396 | * @param v 回车换行的处理方式,如果v为true,将换行符转换为<br/>,将空格转换为&nbsp;;否则将回车替换为空格 397 | * @return 处理后的结果 398 | */ 399 | public static String htmlEncode(String s, final boolean v) { 400 | if (s == null) { 401 | return ""; 402 | } 403 | s = s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\\\"", """); 404 | if (v) { 405 | s = s.replaceAll("\\n|(\\n\\r)|(\\r\\n)", "
"); 406 | s = s.replaceAll("\\s", " "); 407 | } else { 408 | s = s.replaceAll("\\s*(\\n+|(\\n\\r)+|(\\r\\n)+)\\s*", " "); 409 | } 410 | return s; 411 | } 412 | 413 | /** 414 | * 处理字符串。 415 | * 如果第二个参数为false,则返回 StringUtils.htmlEncode(s, true) 的结果;否则只将回车替换为<br> 然后返回 416 | */ 417 | public static String textareaFilter(String s, boolean html) { 418 | if (!html) { 419 | return StringUtils.htmlEncode(s, true); 420 | } else { 421 | if (s == null) { 422 | return ""; 423 | } 424 | s = s.replaceAll("\\n|(\\n\\r)|(\\r\\n)", "
"); 425 | return s; 426 | } 427 | } 428 | 429 | public static String getString(String content) { 430 | if (content == null || content.trim().equals("")) { 431 | return ""; 432 | } 433 | content = content.replaceAll("]+>", ""); //剔出了的标签 434 | content = content.replace(" ", ""); 435 | content = content.replace(".", "."); 436 | content = content.replace("\"", "‘"); 437 | content = content.replace("'", "‘"); 438 | content = content.replaceAll("\\s*|\t|\r|\n", "");//去除字符串中的空格,回车,换行符,制表符 439 | return content; 440 | } 441 | 442 | /** 443 | * 将字符串编码为适合在JavaScript中使用的字符串;将其中的换行符替换为空格 444 | * 445 | * @param s 446 | * @return 447 | */ 448 | public static String jsEncode(String s) { 449 | if (s == null) { 450 | return ""; 451 | } 452 | s = s.replace("\\", "\\\\"); 453 | s = s.replace("\"", "\\\""); 454 | s = s.replace("'", "\\'"); 455 | s = s.replaceAll("\\n+|(\\n\\r)+|(\\r\\n)+|\\r+", " "); 456 | return s; 457 | } 458 | 459 | /** 460 | * 使用 GBK 编码机制将字符串转换为 application/x-www-form-urlencoded 格式 461 | */ 462 | public static String urlEncode(final String s) { 463 | if (s == null) { 464 | return ""; 465 | } 466 | try { 467 | return URLEncoder.encode(s, "GBK"); 468 | } catch (Exception e) { 469 | return s; 470 | } 471 | } 472 | 473 | /** 474 | * 使用指定的编码机制将字符串转换为 application/x-www-form-urlencoded 格式 475 | */ 476 | public static String urlEncode(final String s, final String charset) { 477 | if (s == null) { 478 | return ""; 479 | } 480 | try { 481 | return URLEncoder.encode(s, charset); 482 | } catch (Exception e) { 483 | return s; 484 | } 485 | } 486 | 487 | /** 488 | * 获取指定日期的整数表示。如 2010年2月12日 表示为整数 20100212 489 | */ 490 | public static int intDate(final Date date) { 491 | if (date == null) { 492 | return 0; 493 | } 494 | int r = 0; 495 | r = (date.getYear() + 1900) * 10000 + (date.getMonth() + 1) * 100 + date.getDate(); 496 | return r; 497 | } 498 | 499 | /** 500 | * 获取指定日期时间的整数表示。如某日的下午14:33:22 表示为 143322 501 | * 502 | * @param date 503 | * @return 504 | */ 505 | public static int intTime(final Date date) { 506 | if (date == null) { 507 | return 0; 508 | } 509 | int r = 0; 510 | r = date.getHours() * 10000 + date.getMinutes() * 100 + date.getSeconds(); 511 | return r; 512 | } 513 | 514 | /** 515 | * 按照 yyyy-MM-dd 格式 讲 日期字符串转换为 Date对象 516 | * 517 | * @param dstr 字符串 518 | * @return Date对象;如果指定字符串不能解释为yyyy-MM-dd格式日期,返回null 519 | */ 520 | public static Date parseDate(final String dstr) { 521 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 522 | try { 523 | return df.parse(dstr); 524 | } catch (ParseException e) { 525 | return null; 526 | } 527 | } 528 | 529 | /** 530 | * 把整数转换成日期 531 | * 532 | * @param dint 日期的整数表示 533 | * @param tint 时间的整数表示 534 | * @return Date对象 535 | */ 536 | public static Date dateInt(int dint, int tint) { 537 | if (dint < 19000000 || tint < 0) { 538 | return null; 539 | } 540 | int dd = dint % 100; 541 | dint = dint / 100; 542 | int mm = dint % 100 - 1; 543 | dint = dint / 100; 544 | int yy = dint; 545 | 546 | int ss = tint % 100; 547 | tint = tint / 100; 548 | int mi = tint % 100; 549 | tint = tint / 100; 550 | int hh = tint; 551 | return new GregorianCalendar(yy, mm, dd, hh, mi, ss).getTime(); 552 | } 553 | 554 | /** 555 | * 格式化日期 为: yyyy-MM-dd 格式字符串 556 | * 557 | * @param date Date对象 558 | * @return yyyy-MM-dd格式字符串;如果date为null,返回 “未知日期” 559 | */ 560 | public static String formatDate(final Date date) { 561 | if (date == null) { 562 | return "未知日期"; 563 | } 564 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 565 | return dateFormat.format(date); 566 | } 567 | 568 | /** 569 | * 格式化日期 为: yyyy-MM-dd HH:mm:ss 格式 570 | * 571 | * @param date Date对象 572 | * @return yyyy-MM-dd HH:mm:ss 格式字符串;如果date为null,返回 “未知日期” 573 | */ 574 | public static String formatDatetime(final Date date) { 575 | if (date == null) { 576 | return "未知日期"; 577 | } 578 | DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 579 | return dateFormat.format(date); 580 | } 581 | 582 | /** 583 | * 转换IP地址,将后二段显示为 *.* 584 | * 585 | * @param ip 给定IP字符串 586 | */ 587 | public static String ipView(String ip) { 588 | if (StringUtils.hasText(ip)) { 589 | String[] ss = ip.split("\\."); 590 | if (ss.length > 3) { 591 | ip = ss[0] + "." + ss[1] + ".*.*"; 592 | } 593 | 594 | } else { 595 | ip = ""; 596 | } 597 | return ip; 598 | } 599 | 600 | /** 601 | * 将字符串编码为字节数组,使用GBK编码;如果字符串为null,则返回0长度数组 602 | */ 603 | public static byte[] getBytes(final String s) { 604 | if (s == null) { 605 | return new byte[0]; 606 | } 607 | if (s.length() < 1) { 608 | return new byte[0]; 609 | } 610 | try { 611 | return s.getBytes("GBK"); 612 | } catch (UnsupportedEncodingException e) { 613 | return s.getBytes(); 614 | } 615 | } 616 | 617 | /** 618 | * 将字节数组编码为字符串,使用GBK编码 619 | */ 620 | public static String toString(final byte[] bs) { 621 | try { 622 | return new String(bs, "GBK"); 623 | } catch (UnsupportedEncodingException e) { 624 | return new String(bs); 625 | } 626 | } 627 | 628 | /** 629 | * 将字符串数组使用指定连接符号连接成一个字符串 630 | * 631 | * @param ss 字符串数组 632 | * @param connector 连接符,可以是任意字符串 633 | * @return 结果字符串 634 | */ 635 | public static String concat(final String[] ss, final String connector) { 636 | if (ss == null) { 637 | return ""; 638 | } else if (ss.length < 1) { 639 | return ""; 640 | } else { 641 | StringBuilder sb = new StringBuilder(ss[0]); 642 | for (int i = 1; i < ss.length; i++) { 643 | sb.append(connector).append(ss[i]); 644 | } 645 | return sb.toString(); 646 | } 647 | } 648 | 649 | @SuppressWarnings("unchecked") 650 | public static String concat(final Enumeration ss, final String connector) { 651 | if (ss == null) { 652 | return ""; 653 | } else { 654 | StringBuilder sb = new StringBuilder(); 655 | boolean isfirst = true; 656 | while (ss.hasMoreElements()) { 657 | Object o = ss.nextElement(); 658 | if (!isfirst) { 659 | sb.append(connector); 660 | } else { 661 | isfirst = false; 662 | } 663 | sb.append(o); 664 | } 665 | return sb.toString(); 666 | } 667 | } 668 | 669 | public static String getHostFromUrl(String url) { 670 | if (url == null) return ""; 671 | int i = url.indexOf("://"); 672 | if ("http".equalsIgnoreCase(url.substring(0, 4)) && i >= 4 && i <= 5) { 673 | url = url.substring(i + 3); 674 | } 675 | i = url.indexOf("/"); 676 | if (i > 0) { 677 | url = url.substring(0, i); 678 | } 679 | return url; 680 | } 681 | 682 | /** 683 | * 将浮点数格式化为 小数点后保留2位的格式 684 | * 685 | * @param f 686 | * @return 687 | */ 688 | public static String formatFloat(final float f) { 689 | DecimalFormat dnf = new DecimalFormat("#0.00"); 690 | return dnf.format(f); 691 | } 692 | 693 | /** 694 | * 将字符串以指定子字符串分割为多个子字符串,返回分割后的数组 695 | */ 696 | public static String[] split(final String str, final String connector) { 697 | if (!StringUtils.hasText(str)) { 698 | return new String[0]; 699 | } 700 | ArrayList ss = new ArrayList(); 701 | int i = 0, j = 0; 702 | while ((j = str.indexOf(connector, i)) > -1) { 703 | ss.add(str.substring(i, j)); 704 | i = j + 1; 705 | } 706 | ss.add(str.substring(i)); 707 | return ss.toArray(new String[0]); 708 | } 709 | 710 | /** 711 | * 将指定字符串进行md5加密 712 | */ 713 | public static String md5(final String str) { 714 | if (str == null || str.length() == 0) { 715 | return ""; 716 | } 717 | StringBuilder hexString = new StringBuilder(); 718 | try { 719 | MessageDigest md = MessageDigest.getInstance("MD5"); 720 | md.update(str.getBytes()); 721 | byte[] hash = md.digest(); 722 | for (int i = 0; i < hash.length; i++) { 723 | if ((0xff & hash[i]) < 0x10) { 724 | hexString.append("0").append(Integer.toHexString((0xFF & hash[i]))); 725 | } else { 726 | hexString.append(Integer.toHexString(0xFF & hash[i])); 727 | } 728 | } 729 | } catch (NoSuchAlgorithmException e) { 730 | return ""; 731 | } 732 | return hexString.toString(); 733 | } 734 | 735 | /** 736 | * 将一个Map输出为 一个 json 字符串;这是一个不完整的方法。仅适用于 Map 中只包含简单字符串的情况。 737 | * 请不要将此方法用于更多用途。 738 | */ 739 | public static String getJSON(Map map) { 740 | StringBuilder sb = new StringBuilder(); 741 | sb.append("{"); 742 | boolean commaFlag = false; 743 | for (Object key : map.keySet()) { 744 | if (commaFlag) { 745 | sb.append(","); 746 | } else { 747 | commaFlag = true; 748 | } 749 | sb.append(key).append(":\""); 750 | sb.append(jsEncode(map.get(key).toString())); 751 | sb.append("\""); 752 | } 753 | sb.append("}"); 754 | return sb.toString(); 755 | } 756 | 757 | /** 758 | * 取得一个字符的16进制编码 759 | */ 760 | public static String hex(char ch) { 761 | return Integer.toHexString(ch).toUpperCase(); 762 | } 763 | 764 | /** 765 | * 将指定字符串进行编码。 766 | * 将字符串中的字符转换为Unicode编码的格式 767 | * 768 | * @param string 769 | * @return 770 | */ 771 | public static String encodeString(String string) { 772 | if (string == null) return "null"; 773 | StringBuilder sbr = new StringBuilder(string.length() * 4); 774 | sbr.append("'"); 775 | for (int i = 0, sz = string.length(); i < sz; i++) { 776 | char ch = string.charAt(i); 777 | // handle unicode 778 | if (ch > 0xfff) { 779 | sbr.append("\\u"); 780 | sbr.append(hex(ch)); 781 | } else if (ch > 0xff) { 782 | sbr.append("\\u0"); 783 | sbr.append(hex(ch)); 784 | } else if (ch > 0x7f) { 785 | sbr.append("\\u00"); 786 | sbr.append(hex(ch)); 787 | } else if (ch < 32) { 788 | switch (ch) { 789 | case '\b': 790 | sbr.append('\\'); 791 | sbr.append('b'); 792 | break; 793 | case '\n': 794 | sbr.append('\\'); 795 | sbr.append('n'); 796 | break; 797 | case '\t': 798 | sbr.append('\\'); 799 | sbr.append('t'); 800 | break; 801 | case '\f': 802 | sbr.append('\\'); 803 | sbr.append('f'); 804 | break; 805 | case '\r': 806 | sbr.append('\\'); 807 | sbr.append('r'); 808 | break; 809 | default: 810 | if (ch > 0xf) { 811 | sbr.append("\\u00"); 812 | sbr.append(hex(ch)); 813 | } else { 814 | sbr.append("\\u000"); 815 | sbr.append(hex(ch)); 816 | } 817 | break; 818 | } 819 | } else { 820 | // line. 821 | switch (ch) { 822 | case '\'': 823 | sbr.append('\\'); 824 | sbr.append('\''); 825 | break; 826 | case '"': 827 | sbr.append('\\'); 828 | sbr.append('"'); 829 | break; 830 | case '\\': 831 | sbr.append('\\'); 832 | sbr.append('\\'); 833 | break; 834 | default: 835 | sbr.append(ch); 836 | break; 837 | } 838 | } 839 | } 840 | sbr.append("'"); 841 | return sbr.toString(); 842 | } 843 | 844 | /** 845 | * 将日期格式化为 MM-dd 格式 846 | */ 847 | public static String formatMmdd(Date date) { 848 | if (date == null) { 849 | return ""; 850 | } 851 | DateFormat dateFormat = new SimpleDateFormat("MM-dd"); 852 | return dateFormat.format(date); 853 | } 854 | 855 | /** 856 | * 返回字符串的长度 857 | */ 858 | public static int length(String s) { 859 | return (s == null ? 0 : s.length()); 860 | } 861 | 862 | public static String randomString(int length) { 863 | Random random = new Random(); 864 | StringBuilder sb = new StringBuilder(); 865 | for (int i = 0; i < length; i++) { 866 | int t = random.nextInt(3); 867 | if (i == 0 || t == 1) { 868 | sb.append((char) (random.nextInt(26) + 65)); 869 | } else if (t == 0) { 870 | sb.append((char) (random.nextInt(10) + 48)); 871 | } else { 872 | sb.append((char) (random.nextInt(26) + 97)); 873 | } 874 | } 875 | return sb.toString(); 876 | } 877 | 878 | /** 879 | * 判断一个输入的:yyyy-MM-dd格式的日期是否已经过期 880 | * 881 | * @param DATE1 882 | * @return 883 | */ 884 | public static String compare_date(String DATE1) { 885 | DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); 886 | try { 887 | Date dt1 = df.parse(DATE1); 888 | Date dt2 = new Date(); 889 | if (dt1.getTime() < dt2.getTime()) {//已经过期 890 | return "-"; 891 | } else if (dt1.getTime() > dt2.getTime()) {//未过期 892 | return ""; 893 | } else { 894 | return ""; 895 | } 896 | } catch (Exception exception) { 897 | exception.printStackTrace(); 898 | } 899 | return ""; 900 | } 901 | 902 | /** 903 | * 得到客户端IP 904 | * 905 | * @param request 906 | * @return 907 | */ 908 | public final static String getIp(HttpServletRequest request) { 909 | // HttpServletRequest request = new HttpServletRequest(); 910 | // HttpServletRequest request = ServletContext.getRequest(); 911 | // javax.servlet.ServletContext context = javax.servlet.ServletContext; 912 | String ip = request.getHeader("x-forwarded-for"); 913 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 914 | ip = request.getHeader("Proxy-Client-IP"); 915 | } 916 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 917 | ip = request.getHeader("WL-Proxy-Client-IP"); 918 | } 919 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 920 | ip = request.getRemoteAddr(); 921 | } 922 | return ip; 923 | } 924 | 925 | public final static String getRemortIP(HttpServletRequest request) { 926 | if (request.getHeader("x-forwarded-for") == null) { 927 | return request.getRemoteAddr(); 928 | } 929 | return request.getHeader("x-forwarded-for"); 930 | } 931 | 932 | /** 933 | * 是否找到相关信息 934 | * 935 | * @param inv 被找的字符串 936 | * @param reg 要找的字符串( 正则) 937 | * @return 938 | */ 939 | public final static boolean exist(String inv, String reg) { 940 | if (inv == null || inv.trim().equals("") || reg == null || reg.trim().equals("")) { 941 | return false; 942 | } 943 | Pattern pattern = Pattern.compile(reg); 944 | Matcher m = pattern.matcher(inv); 945 | if (m.find()) { 946 | return true; 947 | } 948 | return false; 949 | } 950 | 951 | /** 952 | * 判断是否包含电话 0571-85120488,85120499 953 | * 954 | * @param inv 955 | * @return 956 | */ 957 | public static boolean exitTel(String inv) { 958 | return exist(inv, "(\\d{4}|\\d{3})-(\\d{7,8}|\\d{7,8})"); 959 | } 960 | 961 | /** 962 | * 判断是否包含手机 133571285477 963 | * 964 | * @param inv 965 | * @return 966 | */ 967 | public static boolean exitMobile(String inv) { 968 | return exist(inv, "(1)(\\d{10})"); 969 | } 970 | 971 | /** 972 | * 判断是否包含 email 973 | * 974 | * @param inv 975 | * @return 976 | */ 977 | public static boolean exitEmail(String inv) { 978 | return exist(inv, "[\\w\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{1,4}"); 979 | } 980 | 981 | /** 982 | * 判断是否包含 域名 983 | * 984 | * @param inv 985 | * @return 986 | */ 987 | public static boolean exitUrl(String inv) { 988 | // return exist(inv, "([-a-zA-Z0-9]+\\.)+[a-zA-Z0-9]{1,4}"); 989 | return exist(inv, "([-a-zA-Z0-9]+\\.)+(com|net|org|mobi|info|biz|cc|tv|asia|me|travel|tel|name|co|so|cn|gov|hk){1}"); 990 | } 991 | 992 | /** 993 | * 寻找第一个图片地址 994 | * 995 | * @param inv 996 | * @return 997 | **/ 998 | public static String findFirstImgUrl(String inv) { 999 | if (inv == null || inv.trim().equals("")) { 1000 | return ""; 1001 | } 1002 | // inv = inv.replaceAll("\n", "").replaceAll("\r", ""); 1003 | String retStr = ""; 1004 | //String reg = "src=(.+?.(jpg|gif|png|bmp|jpeg))"; 1005 | String reg = "(?i)]+?)src=(.+?.(\\.jpg|\\.gif|\\.png|\\.bmp|\\.jpeg))(.+?)>"; 1006 | Pattern pattern = Pattern.compile(reg); 1007 | Matcher m = pattern.matcher(inv); 1008 | while (m.find()) { 1009 | int index = m.start(2); 1010 | int end = m.end(3); 1011 | retStr = inv.substring(index, end); 1012 | retStr = retStr.replace("\"", "").replace("'", "").trim(); 1013 | return retStr; 1014 | } 1015 | return retStr; 1016 | } 1017 | 1018 | /** 1019 | * 寻找第一个视频地址 1020 | * 1021 | * @param inv 1022 | * @return 1023 | **/ 1024 | public static String findFirstVideoUrl(String inv) { 1025 | if (inv == null || inv.trim().equals("")) { 1026 | return ""; 1027 | } 1028 | String retStr = ""; 1029 | //String reg = "src=(.+?.(\\.swf))"; 1030 | // String reg = "(?i)"; 1031 | String reg = "(?i)]*?)src=(.+?.(\\.swf))(.+?)>"; 1032 | Pattern pattern = Pattern.compile(reg); 1033 | Matcher m = pattern.matcher(inv); 1034 | while (m.find()) { 1035 | int index = m.start(2); 1036 | int end = m.end(3); 1037 | retStr = inv.substring(index, end); 1038 | retStr = retStr.replace("\"", "").replace("'", "").trim(); 1039 | return retStr; 1040 | } 1041 | return retStr; 1042 | } 1043 | 1044 | /** 1045 | * 字符串编码转换的实现方法 1046 | * 1047 | * @param str 待转换编码的字符串 1048 | * @param oldCharset 原编码 1049 | * @param newCharset 目标编码 1050 | * @return 1051 | * @throws UnsupportedEncodingException 1052 | */ 1053 | public static String changeCharset(String str, String oldCharset, String newCharset) { 1054 | if (str != null) { 1055 | try { 1056 | //用旧的字符编码解码字符串。解码可能会出现异常。 1057 | byte[] bs = str.getBytes(oldCharset); 1058 | //用新的字符编码生成字符串 1059 | return new String(bs, newCharset); 1060 | } catch (UnsupportedEncodingException e) { 1061 | // TODO Auto-generated catch block 1062 | e.printStackTrace(); 1063 | } 1064 | } 1065 | return str; 1066 | } 1067 | 1068 | public static long percentTotal(long nowNum, long total) { 1069 | if (nowNum <= 0 || total <= 0){ 1070 | return 0; 1071 | } 1072 | if (nowNum == total){ 1073 | return 100; 1074 | } 1075 | return Math.round((Double.valueOf(nowNum) * 100 / Double.valueOf(total))); 1076 | } 1077 | 1078 | /** 1079 | * 得到最短的关键词 1080 | * 1081 | * @param keys 1082 | * @return 1083 | */ 1084 | public static String findShortKey(String keys) { 1085 | if (keys == null || keys.trim().equals("")) { 1086 | return ""; 1087 | } 1088 | keys = keys.replaceAll(",", ","); 1089 | String[] keysArray = StringUtils.split(keys, ","); 1090 | if (keysArray.length <= 1) { 1091 | return StringUtils.nvl(keysArray[0]); 1092 | } 1093 | String minKey = ""; 1094 | for (int i = 0; i < keysArray.length; i++) { 1095 | String tempKey = StringUtils.nvl(keysArray[i]).trim(); 1096 | if (tempKey.equals("")) { 1097 | continue; 1098 | } 1099 | if (minKey.equals("")) { 1100 | minKey = tempKey; 1101 | continue; 1102 | } 1103 | if (minKey.length() > tempKey.length()) { 1104 | minKey = tempKey; 1105 | } 1106 | } 1107 | return minKey; 1108 | } 1109 | 1110 | /** 1111 | * 检查字符串是否是空值 1112 | * 1113 | * @param s 1114 | * @return 1115 | */ 1116 | public static boolean isEmpty(final String s) { 1117 | return s == null || s.length() < 1 || s.trim().length() < 1; 1118 | } 1119 | 1120 | /** 1121 | * 检查字符串是否是空值 1122 | * 1123 | * @param s 1124 | * @return 1125 | */ 1126 | public static boolean isNotEmpty(final String s) { 1127 | return !isEmpty(s); 1128 | } 1129 | 1130 | /** 1131 | * @param string 1132 | * @param sbr 1133 | */ 1134 | public static void appendJsEncoded(String string, Appendable sbr) throws IOException { 1135 | sbr.append("'"); 1136 | for (int i = 0, sz = string.length(); i < sz; i++) { 1137 | char ch = string.charAt(i); 1138 | // handle unicode 1139 | if (ch >= 0x4e00 && ch <= 0x9fa5) { 1140 | sbr.append(ch); 1141 | } else if (ch > 0xfff) { 1142 | sbr.append("\\u"); 1143 | sbr.append(hex(ch)); 1144 | } else if (ch > 0xff) { 1145 | sbr.append("\\u0"); 1146 | sbr.append(hex(ch)); 1147 | } else if (ch > 0x7f) { 1148 | sbr.append("\\u00"); 1149 | sbr.append(hex(ch)); 1150 | } else if (ch < 32) { 1151 | switch (ch) { 1152 | case '\b': 1153 | sbr.append('\\'); 1154 | sbr.append('b'); 1155 | break; 1156 | case '\n': 1157 | sbr.append('\\'); 1158 | sbr.append('n'); 1159 | break; 1160 | case '\t': 1161 | sbr.append('\\'); 1162 | sbr.append('t'); 1163 | break; 1164 | case '\f': 1165 | sbr.append('\\'); 1166 | sbr.append('f'); 1167 | break; 1168 | case '\r': 1169 | sbr.append('\\'); 1170 | sbr.append('r'); 1171 | break; 1172 | default: 1173 | if (ch > 0xf) { 1174 | sbr.append("\\u00"); 1175 | sbr.append(hex(ch)); 1176 | } else { 1177 | sbr.append("\\u000"); 1178 | sbr.append(hex(ch)); 1179 | } 1180 | break; 1181 | } 1182 | } else { 1183 | // line. 1184 | switch (ch) { 1185 | case '\'': 1186 | sbr.append('\\'); 1187 | sbr.append('\''); 1188 | break; 1189 | case '"': 1190 | sbr.append('\\'); 1191 | sbr.append('"'); 1192 | break; 1193 | case '\\': 1194 | sbr.append('\\'); 1195 | sbr.append('\\'); 1196 | break; 1197 | default: 1198 | sbr.append(ch); 1199 | break; 1200 | } 1201 | } 1202 | } 1203 | sbr.append("'"); 1204 | } 1205 | 1206 | 1207 | public static Long[] strArrToLongArr(String[] strings) { 1208 | if (strings == null){ 1209 | return null; 1210 | } 1211 | for (String s : strings) { 1212 | if (!isNumeric(s) && !"".equals(s)) { 1213 | throw new RuntimeException("不可转换为Long"); 1214 | } 1215 | } 1216 | Long[] longs = new Long[strings.length]; 1217 | for (int i = 0; i < strings.length; i++) { 1218 | longs[i] = longValue(strings[i]); 1219 | } 1220 | return longs; 1221 | } 1222 | 1223 | public static String keywordFilter(String keyword) { 1224 | if (keyword == null || keyword.trim().equals("")) { 1225 | return ""; 1226 | } 1227 | keyword = keyword.replaceAll("\\\\", " "); 1228 | keyword = keyword.replaceAll("\\+", " "); 1229 | keyword = keyword.replaceAll("-", " "); 1230 | keyword = keyword.replaceAll("&&", " "); 1231 | keyword = keyword.replaceAll("\\|\\|", " "); 1232 | keyword = keyword.replaceAll("!", " "); 1233 | keyword = keyword.replaceAll("\\(", " "); 1234 | keyword = keyword.replaceAll("\\)", " "); 1235 | keyword = keyword.replaceAll("\\{", " "); 1236 | keyword = keyword.replaceAll("\\}", " "); 1237 | keyword = keyword.replaceAll("\\[", " "); 1238 | keyword = keyword.replaceAll("\\]", " "); 1239 | keyword = keyword.replaceAll("\\^", " "); 1240 | keyword = keyword.replaceAll("\"", " "); 1241 | keyword = keyword.replaceAll("\\~", " "); 1242 | keyword = keyword.replaceAll("\\*", " "); 1243 | keyword = keyword.replaceAll("\\?", " "); 1244 | keyword = keyword.replaceAll("\\:", " "); 1245 | return keyword; 1246 | } 1247 | 1248 | /** 1249 | *判断一个字符串 是否是小数 1250 | * @param num 1251 | * @return 1252 | */ 1253 | public static boolean judgeIsDecimal(String num){ 1254 | boolean isdecimal = false; 1255 | if (num.contains(".")) { 1256 | isdecimal=true; 1257 | } 1258 | return isdecimal; 1259 | } 1260 | 1261 | /** 1262 | * 判断是否是个json 1263 | * @param jsonInString 1264 | * @return 1265 | */ 1266 | public final static boolean isJSON(String jsonInString) { 1267 | try { 1268 | Gson gson = new Gson(); 1269 | gson.fromJson(jsonInString, Object.class); 1270 | return true; 1271 | } catch(JsonSyntaxException ex) { 1272 | return false; 1273 | } 1274 | } 1275 | 1276 | /** 1277 | * 手机号 中间4位使用*替换 1278 | * @param phone 1279 | * @return 1280 | */ 1281 | public static String middleReplaceStar(String phone) { 1282 | String result = null; 1283 | if (!StringUtils.isEmpty(phone)) { 1284 | if (phone.length() < 7) { 1285 | result = phone; 1286 | } else { 1287 | String start = phone.substring(0, 3); 1288 | String end = phone.substring(phone.length() - 4, phone.length()); 1289 | StringBuilder sb = new StringBuilder(); 1290 | sb.append(start).append("****").append(end); 1291 | result = sb.toString(); 1292 | } 1293 | } 1294 | return result; 1295 | } 1296 | 1297 | /** 1298 | * 获取一定长度的随机字符串,范围0-9,a-z 1299 | * 1300 | * @param length:指定字符串长度 1301 | * @return 一定长度的随机字符串 1302 | */ 1303 | public static String getRandomStringByLength(int length) { 1304 | String base = "abcdefghijklmnopqrstuvwxyz0123456789"; 1305 | Random random = new Random(); 1306 | StringBuffer sb = new StringBuffer(); 1307 | for (int i = 0; i < length; i++) { 1308 | int number = random.nextInt(base.length()); 1309 | sb.append(base.charAt(number)); 1310 | } 1311 | return sb.toString(); 1312 | } 1313 | 1314 | public static String humpToUnderline(String para) { 1315 | para = firstCharToLower(para); 1316 | StringBuilder sb = new StringBuilder(para); 1317 | int temp = 0; 1318 | 1319 | for(int i = 0; i < para.length(); ++i) { 1320 | if (Character.isUpperCase(para.charAt(i))) { 1321 | sb.insert(i + temp, "_"); 1322 | ++temp; 1323 | } 1324 | } 1325 | 1326 | return sb.toString().toLowerCase(); 1327 | } 1328 | 1329 | public static String firstCharToLower(String str) { 1330 | char firstChar = str.charAt(0); 1331 | if (firstChar >= 'A' && firstChar <= 'Z') { 1332 | char[] arr = str.toCharArray(); 1333 | arr[0] = (char)(arr[0] + 32); 1334 | return new String(arr); 1335 | } else { 1336 | return str; 1337 | } 1338 | } 1339 | } 1340 | --------------------------------------------------------------------------------