1.移除浙政钉诗画代码 2.增加酒店管理

This commit is contained in:
zjf committed 2026-09-28 22:20:09 +08:00
1 parent 33c93c3e18
commit 1845f5865c
28 files changed
+855 -1160

No files matched your search

@@ -1,254 +1,47 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.bsd.pjcloud.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.admin.api.dto.UserDTO;
import com.bsd.pjcloud.admin.api.dto.UserTokenDTO;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.admin.api.feign.RemoteTokenService;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.app.service.AppUserService;
import com.bsd.pjcloud.canteen.api.feign.RemoteCanteenSpuService;
import com.bsd.pjcloud.canteen.api.feign.RemoteDailyMenuService;
import com.bsd.pjcloud.canteen.api.feign.RemoteOnlineCanteenSpuService;
import com.bsd.pjcloud.canteen.dto.app.DailyMenuDTO;
import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity;
import com.bsd.pjcloud.canteen.vo.app.CanteenOnlineSpuAppVO;
import com.bsd.pjcloud.canteen.vo.app.DailyMenuAppVO;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.thirdparty.api.feign.RemoterThirdPartyAuthService;
import com.bsd.pjcloud.thirdparty.dto.SHJNSSODTO;
import com.bsd.pjcloud.thirdparty.dto.ZZDUserDTO;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyUserEntity;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* app用户表
*
* @author zjf
* @date 2022-12-07 09:52:03
* App 辅助调试接口(原浙政钉/诗画单点登录已移除)。
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/appLogin")
@Tag(description = "appLogin", name = "浙政钉诗画单点登录")
@Tag(description = "appLogin", name = "App登录辅助")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class AppUserAutoLoginController {
// private final AppUserService appUserService;
private final RemoteTokenService remoteTokenService;
private final RemoteUserService remoteUserService;
private final RemoterThirdPartyAuthService remoterThirdPartyAuthService;
private final RemoteCanteenSpuService remoteCanteenSpuService;
private final RemoteOnlineCanteenSpuService remoteOnlineCanteenSpuService;
private final RemoteDailyMenuService remoteDailyMenuService;
/*
*
* @param token 浙政钉前端获取的token
* @return
*/
@GetMapping("/zzdsso/{token}")
@Operation(summary = "浙政钉单点登录")
@Inner(value = false)
@GlobalTransactional
@Transactional(rollbackFor = Exception.class)
//todo 引入seata分布式事务
public R zzdsso(@PathVariable(name = "token") String token) {
//获取数字政务token通过浙政钉前端获取的token,调试阶段先写死
String json = "{" +
"\"username\": \"李宗相\"," +
"\"organizationId\": 2," +
"\"userId\": 1340," +
"\"nickName\": \"李宗相\"," +
"\"mobile\": \"18576496089\"," +
"\"regionCode\": null," +
"\"ext\": {" +
"\"employeeCode\": \"GE_ae12bdf21598498eb48bf2cf2c45a3e1\"," +
"\"organizationCode\": \"GO_63e77077d5e041789bbbe1d0d20bcf41\"," +
"\"nickNameCn\": \"李宗相\"," +
"\"openid\": \"1b51f0e11fc73b8adddcbaf7982daab6\"," +
"\"realmId\": \"196729\"," +
"\"mobile\": null" +
"}" +
"}";
ObjectMapper objectMapper = new ObjectMapper();
ZZDUserDTO zzdUserDTO;
try {
zzdUserDTO = objectMapper.readValue(json, ZZDUserDTO.class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
//暂时先注释,模拟一下就行
// R zzdsso = remoterThirdPartyAuthService.zzdsso(token);
// ZZDUserDTO zzdUserDTO = (ZZDUserDTO) zzdsso.getData();
ZZDUserDTO.Ext ext = zzdUserDTO.getExt();
String employeeCode = ext.getEmployeeCode();
String mobile = zzdUserDTO.getMobile();
//两种方式,手机号+员工钉钉号,查到相同就视为同一个人,然后登录
//先系统表里面查看是否有,在第三方数据库表里面看下是否有
//1.根据手机号或者钉钉号先查询是否有这个人
R<List<SysUser>> userByMobileOrEmployeeCode = remoteUserService.getUserByMobileOrEmployeeCode(employeeCode, mobile);
List<SysUser> sysUserList = userByMobileOrEmployeeCode.getData();
if(!CollectionUtils.isEmpty(sysUserList)){
//登录成功生成token
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUserList.get(0));
remoteTokenService.generateToken(userTokenDTO);
}
//2.根据浙政钉的员工钉钉号查询是否有这个人
R<List<ThirdPartyUserEntity>> thirdPartyUserEntityR=remoterThirdPartyAuthService.getUserByEmployeeCode(employeeCode);
List<ThirdPartyUserEntity> thirdPartyUserEntityList = thirdPartyUserEntityR.getData();
UserDTO userDTO = new UserDTO();
//写死,新加入到部门表的全部归类到其他部门里面,不按照浙政钉的部门
userDTO.setDeptId(1865990541445263361L);
userDTO.setUsername(zzdUserDTO.getUsername());
userDTO.setName(zzdUserDTO.getUsername());
userDTO.setNickname(zzdUserDTO.getUsername());
//浙里办用户
userDTO.setSystemType("1");
//取上面的手机号
userDTO.setPhone(mobile);
userDTO.setDingtalkEmployeeCode(employeeCode);
//随便写死给个密码
userDTO.setPassword("pj"+mobile);
/* if(!CollectionUtils.isEmpty(thirdPartyUserEntityList)){
//todo 这一步现在是没用的,这个最终要考虑是否通过浙政钉里面的人员来限制移动端的使用用户来决定
//第三方数据库里面有
//这里只在第三方库里查到,所以要对系统用户表进行新增当前用户,插入一条信息进去
R<Boolean> booleanR = remoteUserService.registerUser(userDTO);
//通过唯一手机号查询到刚注册的用户信息,生成token
R<List<SysUser>> userByMobile=remoteUserService.getUserByMobile(mobile);
SysUser sysUser = userByMobile.getData().get(0);
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUser);
//登录成功,根据新注册的用户信息返回token
remoteTokenService.generateToken(userTokenDTO);
}
else {
//两个表都没查到人员不能够使用移动端的系统
}*/
// log.info("用户不存在,尝试创建用户");
R<Boolean> booleanR = remoteUserService.registerUser(userDTO);
//通过唯一手机号查询到刚注册的用户信息,生成token
R<List<SysUser>> userByMobile=remoteUserService.getUserByMobile(mobile);
SysUser sysUser = userByMobile.getData().get(0);
int i=8/0;
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUser);
//登录成功,根据新注册的用户信息返回token
return remoteTokenService.generateToken(userTokenDTO);
}
/**
* 诗画sdk获取参数
*
* @param
* @return
*/
@PostMapping("/shjnsso")
@Inner(value = false)
@Operation(summary = "诗画江南单点登录")
public R shjnsso(@RequestBody @Valid SHJNSSODTO shjnsso) {
R rdata = remoterThirdPartyAuthService.shjnsso(shjnsso.getAccountId(), shjnsso.getSessionId());
ZZDUserDTO zzdUserDTO = (ZZDUserDTO) rdata.getData();
ZZDUserDTO.Ext ext = zzdUserDTO.getExt();
String mobile = zzdUserDTO.getMobile();
R<List<SysUser>> rListSysUser = remoteUserService.getUserByMobileOrEmployeeCode(null, mobile);
List<SysUser> sysUsers = rListSysUser.getData();
if(!CollectionUtils.isEmpty(sysUsers)){
//登录成功生成token
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUsers.get(0));
remoteTokenService.generateToken(userTokenDTO);
}
// log.info("用户不存在,尝试创建用户");
// CreateUserDto createUserDto = new CreateUserDto();
// createUserDto.setUsername(zzdUserDTO.getUsername());
// createUserDto.setMobile(mobile);
// createUserDto.setNickname(zzdUserDTO.getNickName());
// createUserDto.setAvatar(ext.getHeadImgUrl());
// //todo 换成枚举
// //诗画用户
// createUserDto.setUserType("2");
// AdminUserRespDTO adminUserRespDTONew=adminUserApi.createSysUser(createUserDto);
// log.info("创建用户成功,用户id为:{}",adminUserRespDTONew.getId());
// return createTokenAfterLoginSuccess(adminUserRespDTONew.getId(), mobile, LoginLogTypeEnum.LOGIN_SSO);
return R.failed("不存在用户");
}
@Inner(value = false)
@GetMapping("/getAuthToken/{token}")
public R info(@PathVariable(name = "token") String token) {
//调用第三方获取基础信息与数据库进行匹配
// 拼接请求参数
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setUsername("admin"); // 只需要用户名,就可以生成 token
userTokenDTO.setAuthorities(List.of("sys_user_add")); // 权限列表的字符串,能调用的接口范围
// feign 调用 auth 模块生成 token
userTokenDTO.setUsername("admin");
userTokenDTO.setAuthorities(List.of("sys_user_add"));
R<Map> authenticationTokenR = remoteTokenService.generateToken(userTokenDTO);
// // 客户端信息
// RegisteredClient registeredClient = data.getRegisteredClient();
Object accessToken = authenticationTokenR.getData().get("accessToken");
Object refreshToken = authenticationTokenR.getData().get("refreshToken");
Map<String, Object> stringObjectHashMap = new HashMap<>();
@@ -257,142 +50,17 @@ public class AppUserAutoLoginController {
return R.ok(stringObjectHashMap);
}
@GetMapping(value = { "/test" })
@Inner(value = false)
public R info() {
// R<List<CanteenOnlineSpuAppVO>> spuListByIds = remoteOnlineCanteenSpuService.getSpuListByIds(Arrays.asList(1876870696924069890L));
// R<List<CanteenSpuEntity>> spuListByIds = remoteCanteenSpuService.getSpuListByIds(Arrays.asList(1876517188510273537L, 1866411313032441857L));
// List<CanteenOnlineSpuAppVO> data = spuListByIds.getData();
Page<Object> objectPage = new Page<>();
objectPage.setCurrent(1);
objectPage.setSize(10);
DailyMenuDTO dailyMenuDTO = new DailyMenuDTO();
dailyMenuDTO.setMerchantId(16L);
dailyMenuDTO.setStartTime(LocalDate.of(2024,12,16));
dailyMenuDTO.setStartTime(LocalDate.of(2024, 12, 16));
R<Page> pageR = remoteDailyMenuService.selectTimeFoodPage(objectPage, dailyMenuDTO);
return R.ok(pageR);
}
//
// /**
// * 分页查询
// * @param page 分页对象
// * @param appUserDTO app用户表
// * @return
// */
// @Operation(summary = "分页查询", description = "分页查询")
// @GetMapping("/page")
// public R getAppUserPage(Page page, AppUserDTO appUserDTO) {
// return R.ok(appUserService.getUsersWithRolePage(page, appUserDTO));
// }
//
// /**
// * 通过id查询app用户表
// * @param userId id
// * @return R
// */
// @Operation(summary = "通过id查询", description = "通过id查询")
// @GetMapping("/details/{userId}")
// public R getById(@PathVariable("userId") Long userId) {
// return R.ok(appUserService.selectUserVoById(userId));
// }
//
// /**
// * @return R
// */
// @Operation(summary = "通过userName查询", description = "通过userName查询")
// @GetMapping("/details")
// public R getByUserName(AppUser user) {
// AppUser one = appUserService.getOne(Wrappers.query(user), false);
// return R.ok(one == null ? null : CommonConstants.SUCCESS);
// }
//
// /**
// * 新增app用户表
// * @param appUser app用户表
// * @return R
// */
// @Operation(summary = "新增app用户表", description = "新增app用户表")
// @SysLog("新增app用户表")
// @PostMapping
// @HasPermission("app_appuser_add")
// public R save(@RequestBody AppUserDTO appUser) {
// appUserService.saveUser(appUser);
// return R.ok();
// }
//
// /**
// * 修改app用户表
// * @param appUser app用户表
// * @return R
// */
// @Operation(summary = "修改app用户表", description = "修改app用户表")
// @SysLog("修改app用户表")
// @PutMapping
// @HasPermission("app_appuser_edit")
// public R updateById(@RequestBody AppUserDTO appUser) {
// return R.ok(appUserService.updateUser(appUser));
// }
//
// /**
// * 通过id删除app用户表
// * @param ids userIds
// * @return R
// */
// @Operation(summary = "通过ids删除app用户表", description = "通过ids删除app用户表")
// @SysLog("通过id删除app用户表")
// @DeleteMapping
// @HasPermission("app_appuser_del")
// public R removeById(@RequestBody Long[] ids) {
// return R.ok(appUserService.deleteAppUserByIds(ids));
// }
//
// /**
// * 导出excel 表格
// * @param appUser 查询条件
// * @return excel 文件流
// */
// @ResponseExcel
// @GetMapping("/export")
// @HasPermission("app_appuser_export")
// public List<AppUserExcelVO> export(AppUserDTO appUser) {
// return appUserService.listUser(appUser);
// }
//
// @SysLog("修改个人信息")
// @PutMapping("/edit")
// public R updateUserInfo(@Valid @RequestBody AppUserDTO userDto) {
// if (UserTypeEnum.TOC.getStatus().equals(SecurityUtils.getUser().getUserType())) {
// userDto.setUserId(SecurityUtils.getUser().getId());
// // TOC 以手机号为key
// userDto.setUsername(SecurityUtils.getUser().getUsername());
// }
// return appUserService.updateUserInfo(userDto);
// }
//
// /**
// * 导入用户
// * @param excelVOList 用户列表
// * @param bindingResult 错误信息列表
// * @return R
// */
// @PostMapping("/import")
// @HasPermission("app_appuser_export")
// public R importUser(@RequestExcel List<AppUserExcelVO> excelVOList, BindingResult bindingResult) {
// return appUserService.importUser(excelVOList, bindingResult);
// }
//
// @Operation(summary = "注册app用户表", description = "注册app用户表")
// @SysLog("注册app用户表")
// @Inner(value = false)
// @PostMapping("/register")
// public R register(@RequestBody AppUserDTO appUser) {
// return appUserService.registerAppUser(appUser);
// }
}
@@ -0,0 +1,66 @@
package com.bsd.pjcloud.canteen.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.bsd.pjcloud.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@Data
@TenantTable
@TableName("canteen_hotel")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "酒店管理")
public class CanteenHotelEntity extends Model<CanteenHotelEntity> {
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键(系统生成)")
private Long id;
private Long tenantId;
private Long deptId;
@Schema(description = "外部Excel中的id")
private String sourceId;
@Schema(description = "集团编码")
private String groupCode;
@Schema(description = "酒店编码")
private String hotelCode;
@Schema(description = "外部集团ID")
private String sGroupId;
@Schema(description = "外部酒店ID")
private String sHotelId;
@Schema(description = "酒店名称")
private String hotelName;
@Schema(description = "导入来源Sheet名")
private String sheetName;
@Schema(description = "备注")
private String remark;
@TableField(fill = FieldFill.INSERT)
private String createBy;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
@TableField(fill = FieldFill.INSERT)
private String delFlag;
}
@@ -0,0 +1,45 @@
package com.bsd.pjcloud.canteen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@Schema(description = "酒店Excel导入任务/结果")
public class CanteenHotelImportResultVO {
@Schema(description = "任务ID(异步导入)")
private String taskId;
@Schema(description = "RUNNING / SUCCESS / FAILED")
private String status;
@Schema(description = "进度或失败说明")
private String message;
@Schema(description = "处理Sheet数")
private int sheetCount;
@Schema(description = "当前已处理Sheet序号(从1开始)")
private int sheetIndex;
@Schema(description = "读取行数(含跳过)")
private int readCount;
@Schema(description = "新增条数")
private int insertCount;
@Schema(description = "更新条数")
private int updateCount;
@Schema(description = "跳过条数")
private int skipCount;
@Schema(description = "Sheet名称列表")
private List<String> sheetNames = new ArrayList<>();
@Schema(description = "跳过样例(最多50条)")
private List<String> skipSamples = new ArrayList<>();
}
@@ -0,0 +1,91 @@
package com.bsd.pjcloud.canteen.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.canteen.entity.CanteenHotelEntity;
import com.bsd.pjcloud.canteen.service.CanteenHotelService;
import com.bsd.pjcloud.canteen.vo.CanteenHotelImportResultVO;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequiredArgsConstructor
@RequestMapping("/canteenHotel")
@Tag(name = "酒店管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class CanteenHotelController {
private final CanteenHotelService hotelService;
@Operation(summary = "分页查询")
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_view')")
public R<Page<CanteenHotelEntity>> page(@ParameterObject Page<CanteenHotelEntity> page,
@ParameterObject CanteenHotelEntity query) {
var wrapper = Wrappers.<CanteenHotelEntity>lambdaQuery()
.like(StrUtil.isNotBlank(query.getHotelName()), CanteenHotelEntity::getHotelName, query.getHotelName())
.like(StrUtil.isNotBlank(query.getHotelCode()), CanteenHotelEntity::getHotelCode, query.getHotelCode())
.like(StrUtil.isNotBlank(query.getGroupCode()), CanteenHotelEntity::getGroupCode, query.getGroupCode())
.eq(StrUtil.isNotBlank(query.getSourceId()), CanteenHotelEntity::getSourceId, query.getSourceId())
.eq(StrUtil.isNotBlank(query.getSheetName()), CanteenHotelEntity::getSheetName, query.getSheetName())
.orderByDesc(CanteenHotelEntity::getCreateTime)
.orderByDesc(CanteenHotelEntity::getId);
return R.ok(hotelService.pageScope(page, wrapper));
}
@Operation(summary = "详情")
@GetMapping("/{id}")
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_view')")
public R getById(@PathVariable Long id) {
return R.ok(hotelService.getById(id));
}
@SysLog("新增酒店")
@Operation(summary = "新增")
@PostMapping
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_add')")
public R save(@RequestBody CanteenHotelEntity entity) {
return R.ok(hotelService.saveHotel(entity));
}
@SysLog("修改酒店")
@Operation(summary = "修改")
@PutMapping
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_edit')")
public R update(@RequestBody CanteenHotelEntity entity) {
return R.ok(hotelService.updateHotel(entity));
}
@SysLog("删除酒店")
@Operation(summary = "删除")
@DeleteMapping
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_del')")
public R remove(@RequestBody Long[] ids) {
return R.ok(hotelService.removeHotels(ids));
}
/** 上传文件会立刻 transferTo 临时目录,勿加 @SysLog,否则会序列化已删除的 Undertow 临时文件报 NoSuchFileException */
@Operation(summary = "提交Excel异步导入(多Sheet流式)")
@PostMapping("/import")
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_import')")
public R<CanteenHotelImportResultVO> importExcel(@RequestParam("file") MultipartFile file) {
return hotelService.startImport(file);
}
@Operation(summary = "查询酒店导入进度")
@GetMapping("/import/status/{taskId}")
@PreAuthorize("@pms.hasPermission('canteen_canteenHotel_import')")
public R<CanteenHotelImportResultVO> importStatus(@PathVariable String taskId) {
return hotelService.getImportStatus(taskId);
}
}
@@ -0,0 +1,14 @@
package com.bsd.pjcloud.canteen.mapper;
import com.bsd.pjcloud.canteen.entity.CanteenHotelEntity;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface CanteenHotelMapper extends PjcloudBaseMapper<CanteenHotelEntity> {
int insertBatch(@Param("list") List<CanteenHotelEntity> list);
}
@@ -0,0 +1,26 @@
package com.bsd.pjcloud.canteen.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.canteen.entity.CanteenHotelEntity;
import com.bsd.pjcloud.canteen.vo.CanteenHotelImportResultVO;
import com.bsd.pjcloud.common.core.util.R;
import org.springframework.web.multipart.MultipartFile;
public interface CanteenHotelService extends IService<CanteenHotelEntity> {
Page<CanteenHotelEntity> pageScope(Page<CanteenHotelEntity> page, LambdaQueryWrapper<CanteenHotelEntity> wrapper);
Boolean saveHotel(CanteenHotelEntity entity);
Boolean updateHotel(CanteenHotelEntity entity);
Boolean removeHotels(Long[] ids);
/** 提交异步导入,立刻返回 taskId(status=RUNNING) */
R<CanteenHotelImportResultVO> startImport(MultipartFile file);
/** 查询导入任务进度 */
R<CanteenHotelImportResultVO> getImportStatus(String taskId);
}
@@ -0,0 +1,493 @@
package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.canteen.entity.CanteenHotelEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenHotelMapper;
import com.bsd.pjcloud.canteen.service.CanteenHotelService;
import com.bsd.pjcloud.canteen.vo.CanteenHotelImportResultVO;
import com.bsd.pjcloud.common.core.constant.CommonConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.data.tenant.TenantContextHolder;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
@RequiredArgsConstructor
public class CanteenHotelServiceImpl extends ServiceImpl<CanteenHotelMapper, CanteenHotelEntity>
implements CanteenHotelService {
/** 多值 INSERT 一批条数(与人脸明细导入同量级) */
private static final int BATCH = 1500;
private static final int SKIP_SAMPLE_LIMIT = 50;
private static final ConcurrentHashMap<String, CanteenHotelImportResultVO> IMPORT_TASKS = new ConcurrentHashMap<>();
@Override
public Page<CanteenHotelEntity> pageScope(Page<CanteenHotelEntity> page,
LambdaQueryWrapper<CanteenHotelEntity> wrapper) {
return baseMapper.selectPageByScope(page, wrapper, DataScope.of());
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveHotel(CanteenHotelEntity entity) {
normalize(entity);
if (StrUtil.isBlank(entity.getHotelName()) && StrUtil.isBlank(entity.getHotelCode())) {
throw new IllegalArgumentException("酒店名称与酒店编码不能同时为空");
}
if (StrUtil.isNotBlank(entity.getHotelCode()) && existsHotelCode(entity.getHotelCode(), null)) {
throw new IllegalArgumentException("酒店编码已存在:" + entity.getHotelCode());
}
if (entity.getDeptId() == null) {
entity.setDeptId(SecurityUtils.getUser().getDeptId());
}
return baseMapper.insert(entity) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateHotel(CanteenHotelEntity entity) {
if (entity.getId() == null) {
throw new IllegalArgumentException("酒店ID不能为空");
}
CanteenHotelEntity old = baseMapper.selectById(entity.getId());
if (old == null) {
throw new IllegalArgumentException("酒店不存在");
}
normalize(entity);
if (StrUtil.isBlank(entity.getHotelName()) && StrUtil.isBlank(entity.getHotelCode())) {
throw new IllegalArgumentException("酒店名称与酒店编码不能同时为空");
}
if (StrUtil.isNotBlank(entity.getHotelCode()) && existsHotelCode(entity.getHotelCode(), entity.getId())) {
throw new IllegalArgumentException("酒店编码已存在:" + entity.getHotelCode());
}
return baseMapper.updateById(entity) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean removeHotels(Long[] ids) {
if (ids == null || ids.length == 0) {
return Boolean.TRUE;
}
return removeByIds(CollUtil.toList(ids));
}
@Override
public R<CanteenHotelImportResultVO> startImport(MultipartFile file) {
if (file == null || file.isEmpty()) {
return R.failed("请上传酒店 Excel");
}
String taskId = IdUtil.getSnowflakeNextIdStr();
CanteenHotelImportResultVO vo = new CanteenHotelImportResultVO();
vo.setTaskId(taskId);
vo.setStatus("RUNNING");
vo.setMessage("已提交,后台导入中…");
IMPORT_TASKS.put(taskId, vo);
File temp;
try {
temp = Files.createTempFile("canteen-hotel-", ".xlsx").toFile();
file.transferTo(temp);
}
catch (Exception e) {
vo.setStatus("FAILED");
vo.setMessage("接收上传文件失败:" + e.getMessage());
return R.failed(vo.getMessage());
}
Long tenantId = TenantContextHolder.getTenantId();
PjcloudUser user = SecurityUtils.getUser();
Long deptId = user != null ? user.getDeptId() : null;
String operator = user != null ? user.getUsername() : "system";
final File tempFile = temp;
Thread worker = new Thread(() -> {
TenantContextHolder.setTenantId(tenantId);
try {
doImportAsync(tempFile, vo, deptId, tenantId, operator);
}
catch (Throwable e) {
log.error("酒店Excel异步导入失败 taskId={}", taskId, e);
vo.setStatus("FAILED");
vo.setMessage(StrUtil.maxLength(StrUtil.blankToDefault(e.getMessage(), e.getClass().getSimpleName()), 500));
}
finally {
TenantContextHolder.clear();
if (tempFile.exists()) {
FileUtil.del(tempFile);
}
}
}, "canteen-hotel-import-" + taskId);
worker.setDaemon(false);
worker.start();
return R.ok(vo, "已提交导入,请稍候查看进度");
}
@Override
public R<CanteenHotelImportResultVO> getImportStatus(String taskId) {
if (StrUtil.isBlank(taskId)) {
return R.failed("taskId 不能为空");
}
CanteenHotelImportResultVO vo = IMPORT_TASKS.get(taskId);
if (vo == null) {
return R.failed("导入任务不存在或已过期,请重新导入");
}
return R.ok(vo);
}
/**
* 一次打开 Excel 读完全部 Sheet;库内先预加载编码索引,再按批 multi-values INSERT / 批量 UPDATE。
*/
private void doImportAsync(File tempFile, CanteenHotelImportResultVO result, Long deptId, Long tenantId,
String operator) throws Exception {
long t0 = System.currentTimeMillis();
List<ReadSheet> sheetMetas = listSheetMetas(tempFile);
if (CollUtil.isEmpty(sheetMetas)) {
result.setStatus("FAILED");
result.setMessage("Excel 中没有任何 Sheet");
return;
}
result.setSheetCount(sheetMetas.size());
result.setMessage("预加载已有酒店索引…");
Map<String, Long> codeIdMap = new HashMap<>();
Map<String, Long> sourceIdMap = new HashMap<>();
preloadIndex(codeIdMap, sourceIdMap);
List<CanteenHotelEntity> buffer = new ArrayList<>(BATCH);
ImportFlushContext ctx = new ImportFlushContext(result, buffer, codeIdMap, sourceIdMap, deptId, tenantId,
operator);
result.setMessage("解析 Excel(共 " + sheetMetas.size() + " 个 Sheet)…");
try (FileInputStream in = new FileInputStream(tempFile)) {
ExcelReader reader = EasyExcel.read(in).build();
try {
List<ReadSheet> readSheets = new ArrayList<>(sheetMetas.size());
int index = 0;
for (ReadSheet meta : sheetMetas) {
index++;
int sheetNo = meta.getSheetNo() == null ? 0 : meta.getSheetNo();
String sheetName = StrUtil.blankToDefault(meta.getSheetName(), "Sheet" + (sheetNo + 1));
result.getSheetNames().add(sheetName);
HotelSheetListener listener = new HotelSheetListener(sheetName, index, ctx);
readSheets.add(EasyExcel.readSheet(sheetNo)
.headRowNumber(1)
.registerReadListener(listener)
.build());
}
reader.read(readSheets);
}
finally {
reader.finish();
}
}
flushBuffer(ctx);
long cost = System.currentTimeMillis() - t0;
result.setStatus("SUCCESS");
result.setMessage(String.format("导入完成:Sheet %d 个,读取 %d 行,新增 %d,更新 %d,跳过 %d,耗时 %d ms",
result.getSheetCount(), result.getReadCount(), result.getInsertCount(), result.getUpdateCount(),
result.getSkipCount(), cost));
log.info("酒店导入完成 taskId={} insert={} update={} skip={} costMs={}", result.getTaskId(),
result.getInsertCount(), result.getUpdateCount(), result.getSkipCount(), cost);
}
private void preloadIndex(Map<String, Long> codeIdMap, Map<String, Long> sourceIdMap) {
List<CanteenHotelEntity> existing = baseMapper.selectList(Wrappers.<CanteenHotelEntity>lambdaQuery()
.select(CanteenHotelEntity::getId, CanteenHotelEntity::getHotelCode, CanteenHotelEntity::getSourceId));
for (CanteenHotelEntity e : existing) {
if (StrUtil.isNotBlank(e.getHotelCode())) {
codeIdMap.putIfAbsent(e.getHotelCode(), e.getId());
}
if (StrUtil.isNotBlank(e.getSourceId())) {
sourceIdMap.putIfAbsent(e.getSourceId(), e.getId());
}
}
}
private static List<ReadSheet> listSheetMetas(File tempFile) throws Exception {
try (FileInputStream metaIn = new FileInputStream(tempFile)) {
ExcelReader metaReader = EasyExcel.read(metaIn).build();
try {
return metaReader.excelExecutor().sheetList();
}
finally {
metaReader.finish();
}
}
}
private void flushBuffer(ImportFlushContext ctx) {
List<CanteenHotelEntity> buffer = ctx.buffer;
if (buffer.isEmpty()) {
return;
}
LocalDateTime now = LocalDateTime.now();
List<CanteenHotelEntity> toInsert = new ArrayList<>();
List<CanteenHotelEntity> toUpdate = new ArrayList<>();
for (CanteenHotelEntity entity : buffer) {
Long existsId = null;
if (StrUtil.isNotBlank(entity.getHotelCode())) {
existsId = ctx.codeIdMap.get(entity.getHotelCode());
}
if (existsId == null && StrUtil.isNotBlank(entity.getSourceId())) {
existsId = ctx.sourceIdMap.get(entity.getSourceId());
}
if (existsId != null) {
entity.setId(existsId);
entity.setUpdateBy(ctx.operator);
entity.setUpdateTime(now);
toUpdate.add(entity);
ctx.result.setUpdateCount(ctx.result.getUpdateCount() + 1);
}
else {
entity.setId(IdWorker.getId());
entity.setTenantId(ctx.tenantId);
entity.setDeptId(ctx.deptId);
entity.setCreateBy(ctx.operator);
entity.setUpdateBy(ctx.operator);
entity.setCreateTime(now);
entity.setUpdateTime(now);
entity.setDelFlag(CommonConstants.STATUS_NORMAL);
toInsert.add(entity);
ctx.result.setInsertCount(ctx.result.getInsertCount() + 1);
if (StrUtil.isNotBlank(entity.getHotelCode())) {
ctx.codeIdMap.put(entity.getHotelCode(), entity.getId());
}
if (StrUtil.isNotBlank(entity.getSourceId())) {
ctx.sourceIdMap.put(entity.getSourceId(), entity.getId());
}
}
}
if (CollUtil.isNotEmpty(toInsert)) {
// 多值 INSERT,避免 saveBatch 逐条/小批
for (int i = 0; i < toInsert.size(); i += BATCH) {
List<CanteenHotelEntity> slice = toInsert.subList(i, Math.min(i + BATCH, toInsert.size()));
baseMapper.insertBatch(slice);
}
}
if (CollUtil.isNotEmpty(toUpdate)) {
updateBatchById(toUpdate, BATCH);
for (CanteenHotelEntity entity : toUpdate) {
if (StrUtil.isNotBlank(entity.getHotelCode())) {
ctx.codeIdMap.put(entity.getHotelCode(), entity.getId());
}
if (StrUtil.isNotBlank(entity.getSourceId())) {
ctx.sourceIdMap.put(entity.getSourceId(), entity.getId());
}
}
}
buffer.clear();
}
private boolean existsHotelCode(String hotelCode, Long excludeId) {
LambdaQueryWrapper<CanteenHotelEntity> w = Wrappers.<CanteenHotelEntity>lambdaQuery()
.eq(CanteenHotelEntity::getHotelCode, hotelCode);
if (excludeId != null) {
w.ne(CanteenHotelEntity::getId, excludeId);
}
return baseMapper.selectCount(w) > 0;
}
private static void normalize(CanteenHotelEntity entity) {
entity.setSourceId(trimToNull(entity.getSourceId()));
entity.setGroupCode(trimToNull(entity.getGroupCode()));
entity.setHotelCode(trimToNull(entity.getHotelCode()));
entity.setSGroupId(trimToNull(entity.getSGroupId()));
entity.setSHotelId(trimToNull(entity.getSHotelId()));
entity.setHotelName(trimToNull(entity.getHotelName()));
entity.setSheetName(trimToNull(entity.getSheetName()));
entity.setRemark(trimToNull(entity.getRemark()));
}
private static String trimToNull(String v) {
return StrUtil.isBlank(v) ? null : v.trim();
}
private static void addSkipSample(CanteenHotelImportResultVO result, String sheet, int excelRow, String reason) {
if (result.getSkipSamples().size() >= SKIP_SAMPLE_LIMIT) {
return;
}
result.getSkipSamples().add(sheet + " 第" + excelRow + "行:" + reason);
}
private final class ImportFlushContext {
final CanteenHotelImportResultVO result;
final List<CanteenHotelEntity> buffer;
final Map<String, Long> codeIdMap;
final Map<String, Long> sourceIdMap;
final Long deptId;
final Long tenantId;
final String operator;
ImportFlushContext(CanteenHotelImportResultVO result, List<CanteenHotelEntity> buffer,
Map<String, Long> codeIdMap, Map<String, Long> sourceIdMap, Long deptId, Long tenantId,
String operator) {
this.result = result;
this.buffer = buffer;
this.codeIdMap = codeIdMap;
this.sourceIdMap = sourceIdMap;
this.deptId = deptId;
this.tenantId = tenantId;
this.operator = operator;
}
}
private final class HotelSheetListener extends AnalysisEventListener<Map<Integer, String>> {
private final String sheetName;
private final int sheetIndex;
private final ImportFlushContext ctx;
private final Map<Integer, String> fieldByCol = new HashMap<>();
private String headError;
HotelSheetListener(String sheetName, int sheetIndex, ImportFlushContext ctx) {
this.sheetName = sheetName;
this.sheetIndex = sheetIndex;
this.ctx = ctx;
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
ctx.result.setSheetIndex(sheetIndex);
fieldByCol.clear();
if (headMap == null || headMap.isEmpty()) {
headError = "表头为空";
return;
}
for (Map.Entry<Integer, String> e : headMap.entrySet()) {
String field = resolveField(e.getValue());
if (field != null) {
fieldByCol.put(e.getKey(), field);
}
}
if (!fieldByCol.containsValue("hotelName") && !fieldByCol.containsValue("hotelCode")
&& !fieldByCol.containsValue("sourceId")) {
headError = "未识别到 id / hotel_code / hotel_name 等必要表头";
}
if (StrUtil.isNotBlank(headError)) {
ctx.result.setSkipCount(ctx.result.getSkipCount() + 1);
addSkipSample(ctx.result, sheetName, 1, headError);
}
else {
ctx.result.setMessage("正在导入 Sheet「" + sheetName + "」(" + sheetIndex + "/"
+ ctx.result.getSheetCount() + ")…");
}
}
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
if (StrUtil.isNotBlank(headError) || data == null) {
return;
}
int excelRow = context.readRowHolder().getRowIndex() + 1;
ctx.result.setReadCount(ctx.result.getReadCount() + 1);
String sourceId = null;
String groupCode = null;
String hotelCode = null;
String sGroupId = null;
String sHotelId = null;
String hotelName = null;
for (Map.Entry<Integer, String> e : data.entrySet()) {
String field = fieldByCol.get(e.getKey());
if (field == null) {
continue;
}
String val = StrUtil.trim(e.getValue());
switch (field) {
case "sourceId" -> sourceId = val;
case "groupCode" -> groupCode = val;
case "hotelCode" -> hotelCode = val;
case "sGroupId" -> sGroupId = val;
case "sHotelId" -> sHotelId = val;
case "hotelName" -> hotelName = val;
default -> {
}
}
}
boolean blank = StrUtil.isBlank(sourceId) && StrUtil.isBlank(groupCode) && StrUtil.isBlank(hotelCode)
&& StrUtil.isBlank(sGroupId) && StrUtil.isBlank(sHotelId) && StrUtil.isBlank(hotelName);
if (blank) {
ctx.result.setSkipCount(ctx.result.getSkipCount() + 1);
return;
}
if (StrUtil.isBlank(hotelName) && StrUtil.isBlank(hotelCode) && StrUtil.isBlank(sourceId)) {
ctx.result.setSkipCount(ctx.result.getSkipCount() + 1);
addSkipSample(ctx.result, sheetName, excelRow, "缺少酒店名称/编码/外部id");
return;
}
CanteenHotelEntity entity = new CanteenHotelEntity();
entity.setSourceId(trimToNull(sourceId));
entity.setGroupCode(trimToNull(groupCode));
entity.setHotelCode(trimToNull(hotelCode));
entity.setSGroupId(trimToNull(sGroupId));
entity.setSHotelId(trimToNull(sHotelId));
entity.setHotelName(trimToNull(hotelName));
entity.setSheetName(sheetName);
ctx.buffer.add(entity);
if (ctx.buffer.size() >= BATCH) {
flushBuffer(ctx);
ctx.result.setMessage(String.format("Sheet「%s」已落库约 %d 行(新增 %d / 更新 %d)…", sheetName,
ctx.result.getReadCount(), ctx.result.getInsertCount(), ctx.result.getUpdateCount()));
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 外层统一 flush
}
private static String resolveField(String header) {
if (StrUtil.isBlank(header)) {
return null;
}
String h = header.trim().toLowerCase(Locale.ROOT).replace(" ", "").replace("_", "");
Map<String, String> alias = new LinkedHashMap<>();
alias.put("id", "sourceId");
alias.put("sourceid", "sourceId");
alias.put("外部id", "sourceId");
alias.put("groupcode", "groupCode");
alias.put("集团编码", "groupCode");
alias.put("hotelcode", "hotelCode");
alias.put("酒店编码", "hotelCode");
alias.put("sgroupid", "sGroupId");
alias.put("外部集团id", "sGroupId");
alias.put("shotelid", "sHotelId");
alias.put("外部酒店id", "sHotelId");
alias.put("hotelname", "hotelName");
alias.put("酒店名称", "hotelName");
alias.put("酒店名", "hotelName");
return alias.get(h);
}
}
}
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.canteen.mapper.CanteenHotelMapper">
<insert id="insertBatch">
INSERT INTO canteen_hotel (
id, tenant_id, dept_id, source_id, group_code, hotel_code, s_group_id, s_hotel_id,
hotel_name, sheet_name, remark, create_by, create_time, update_by, update_time, del_flag
) VALUES
<foreach collection="list" item="e" separator=",">
(
#{e.id}, #{e.tenantId}, #{e.deptId}, #{e.sourceId}, #{e.groupCode}, #{e.hotelCode},
#{e.sGroupId}, #{e.sHotelId}, #{e.hotelName}, #{e.sheetName}, #{e.remark},
#{e.createBy}, #{e.createTime}, #{e.updateBy}, #{e.updateTime}, #{e.delFlag}
)
</foreach>
</insert>
</mapper>
@@ -1,62 +1,26 @@
/*
*
* Copyright (c) 2018-2025, pjcloud All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: pjcloud
*
*/
package com.bsd.pjcloud.thirdparty.api.feign;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.feign.annotation.NoToken;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyUserEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* @author pjcloud
* @date 2018/6/22
* 第三方用户相关 Feign(浙政钉/诗画单点已移除)。
*/
@FeignClient(contextId = "remoterThirdPartyAuthService", value = ServiceNameConstants.THIRDPARTY_SERVICE)
public interface RemoterThirdPartyAuthService {
/**
* zzd单点
* @param token
* @return
*/
@NoToken
@GetMapping("/thirdPartyOAth2/zzd/sso")
R zzdsso(@RequestParam("token") String token);
@NoToken
@GetMapping("/thirdPartyOAth2/zzd/shjnsso")
R shjnsso(@RequestParam("accountId") String accountId,@RequestParam("sessionId") String sessionId);
/**
* 根据钉钉号查询第三方表里面是否有这个人,也就是浙政钉总体系人员表
* @param employeeCode
* @return
* 按员工编码查询第三方用户表
*/
@NoToken
@GetMapping("/thirdPartyUser/getUserByEmployeeCode")
R<List<ThirdPartyUserEntity>> getUserByEmployeeCode(@RequestParam("employeeCode") String employeeCode);
}
@@ -1,21 +0,0 @@
package com.bsd.pjcloud.thirdparty.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 出库入口
*/
@AllArgsConstructor
@Getter
public enum UserTypeEnum {
ZZD("1", "浙政钉用户登录失败"),
SH("2", "诗画用户登录失败"),
;
private final String type;
private final String description;
}
@@ -1,14 +0,0 @@
package com.bsd.pjcloud.thirdparty.dto;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class SHJNSSODTO {
@NotNull(message = "账号id不能为空")
private String accountId;
@NotNull(message = "会员id不能为空")
private String sessionId;
}
@@ -1,9 +0,0 @@
package com.bsd.pjcloud.thirdparty.dto;
import lombok.Data;
@Data
public class UrlTokenDTO {
private String token;
}
@@ -1,11 +0,0 @@
package com.bsd.pjcloud.thirdparty.dto;
import lombok.Data;
@Data
public class ZZDResponse {
private String code;
private String description;
private String data;
}
@@ -1,17 +0,0 @@
package com.bsd.pjcloud.thirdparty.dto;
import lombok.Data;
@Data
public class ZZDUser {
private String username;
private int organizationId;
private int userId;
private String nickName;
private String mobile;
private String regionCode;
private String ext;
private String employeeCode;
}
@@ -1,28 +0,0 @@
package com.bsd.pjcloud.thirdparty.dto;
import lombok.Data;
@Data
public class ZZDUserDTO {
private String username;
private int organizationId;
private int userId;
private String nickName;
private String mobile;
private String regionCode;
private Ext ext;
@Data
public class Ext {
private String employeeCode;
private String organizationCode;
private String nickNameCn;
private String openid;
private String realmId;
private String mobile;
//以下字段,诗画独有
private String headImgUrl;
}
}
@@ -10,7 +10,7 @@ import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 浙政钉用户表
* 第三方用户表
*
* @author zjf
* @date 2024-12-03 13:38:08
@@ -19,7 +19,7 @@ import java.time.LocalDateTime;
@TenantTable
@TableName("third_party_user")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "浙政钉用户表")
@Schema(description = "第三方用户表")
public class ThirdPartyUserEntity extends Model<ThirdPartyUserEntity> {
@@ -68,9 +68,9 @@ public class ThirdPartyUserEntity extends Model<ThirdPartyUserEntity> {
private String nickName;
/**
* 员工浙政钉编码
* 员工钉钉编码
*/
@Schema(description="员工浙政钉编码")
@Schema(description="员工钉钉编码")
private String dingtalkEmployeeCode;
/**
@@ -23,15 +23,4 @@ public class OAuth2ClientConfig {
private String userDetailUrl;
/**
* 浙政钉获取token的接口
*/
private String zzdTokenUrl;
/**
* 浙政钉跟诗画的获取用户信息的url(诗画已直连,保留兼容)
*/
private String zzdShUserInfoUrl;
}
@@ -1,11 +1,8 @@
package com.bsd.pjcloud.thirdparty.controller;
import com.bsd.pjcloud.admin.api.dto.UserTokenDTO;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.admin.api.entity.UserTrustedDeviceEntity;
import com.bsd.pjcloud.admin.api.feign.RemoteTokenService;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.admin.api.feign.RemoteUserTrustedDeviceService;
import com.bsd.pjcloud.common.core.constant.CacheConstants;
import com.bsd.pjcloud.common.core.util.DeviceFingerprintUtil;
@@ -14,336 +11,112 @@ import com.bsd.pjcloud.common.core.util.WebUtils;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.thirdparty.dto.DeviceVerifyDTO;
import com.bsd.pjcloud.thirdparty.dto.SHJNSSODTO;
import com.bsd.pjcloud.thirdparty.dto.UrlTokenDTO;
import com.bsd.pjcloud.thirdparty.dto.ZZDUserDTO;
import com.bsd.pjcloud.thirdparty.service.SmsService;
import com.bsd.pjcloud.thirdparty.service.ThirdPartyOAuth2Service;
import com.bsd.pjcloud.thirdparty.service.ThirdPartyUserService;
import com.bsd.pjcloud.thirdparty.utils.SmsCodeUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
@RequiredArgsConstructor
@RequestMapping("/thirdPartyOAth2" )
@Tag(description = "thirdPartyOAth2" , name = "第三方单点接口" )
@RequestMapping("/thirdPartyOAth2")
@Tag(description = "thirdPartyOAth2", name = "第三方登录辅助接口")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class ThirdPartyOAth2Cotroller {
private final ThirdPartyOAuth2Service thirdPartyOAuth2Service;
private final RemoteTokenService remoteTokenService;
private final RemoteUserService remoteUserService;
private final SmsService smsService;
private final RemoteTokenService remoteTokenService;
private final RemoteUserTrustedDeviceService remoteUserTrustedDeviceService;
private final ThirdPartyUserService thirdPartyUserService;
@Autowired
private RedisTemplate redisTemplate;
private final SmsService smsService;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
private final RemoteUserTrustedDeviceService remoteUserTrustedDeviceService;
// @Operation(summary = "浙政钉token" )
// @GetMapping("/zzd/sso" )
// public R getThirdPartyzzdsso(@RequestParam("token")String token) {
// return R.ok(thirdPartyOAuth2Service.getZZDUserInfo(token));
//
// }
@Operation(summary = "诗画token" )
@GetMapping("/sh/sso" )
public R getThirdPartyshsso(@RequestParam("accountId") String accountId,@RequestParam("sessionId") String sessionId) {
return R.ok(thirdPartyOAuth2Service.shjnsso(accountId,sessionId));
}
/*
*
* @param token 浙政钉前端获取的token
* @return
*/
@PostMapping("/zzdsso")
@Operation(summary = "浙政钉单点登录")
@SysLog("浙政钉单点登录")
@Inner(value = false)
@GlobalTransactional
@Transactional(rollbackFor = Exception.class)
public R zzdsso(@RequestBody UrlTokenDTO urlTokenDTO) {
String token = urlTokenDTO.getToken();
if (StringUtils.isBlank(token)){
return R.failed("token不能为空");
}
//获取数字政务token通过浙政钉前端获取的token,调试阶段先写死
// String json = "{" +
// "\"username\": \"李宗相\"," +
// "\"organizationId\": 2," +
// "\"userId\": 1340," +
// "\"nickName\": \"李宗相\"," +
// "\"mobile\": \"18576496089\"," +
// "\"regionCode\": null," +
// "\"ext\": {" +
// "\"employeeCode\": \"GE_ae12bdf21598498eb48bf2cf2c45a3e1\"," +
// "\"organizationCode\": \"GO_63e77077d5e041789bbbe1d0d20bcf41\"," +
// "\"nickNameCn\": \"李宗相\"," +
// "\"openid\": \"1b51f0e11fc73b8adddcbaf7982daab6\"," +
// "\"realmId\": \"196729\"," +
// "\"mobile\": null" +
// "}" +
// "}";
//
//
// ObjectMapper objectMapper = new ObjectMapper();
// ZZDUserDTO zzdUserDTO;
// try {
// zzdUserDTO = objectMapper.readValue(json, ZZDUserDTO.class);
// } catch (JsonProcessingException e) {
// throw new RuntimeException(e);
// }
//暂时先注释,模拟一下就行
ZZDUserDTO zzdUserDTO = thirdPartyOAuth2Service.zzdsso(token);
ZZDUserDTO.Ext ext = zzdUserDTO.getExt();
String employeeCode = ext.getEmployeeCode();
String mobile = zzdUserDTO.getMobile();
//两种方式,手机号+员工钉钉号,查到相同就视为同一个人,然后登录
//先系统表里面查看是否有,在第三方数据库表里面看下是否有
R<Map> r;
//1.根据手机号或者钉钉号先查询是否有这个人
R<List<SysUser>> userByMobileOrEmployeeCode = remoteUserService.getUserByMobileOrEmployeeCode(employeeCode, mobile);
List<SysUser> sysUserList = userByMobileOrEmployeeCode.getData();
if(!CollectionUtils.isEmpty(sysUserList)){
//登录成功生成token
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUserList.get(0));
r = remoteTokenService.generateToken(userTokenDTO);
}else {
//2.根据浙政钉的员工钉钉号查询是否有这个人
// List<ThirdPartyUserEntity> thirdPartyUserEntityList = thirdPartyUserService.getUserByEmployeeCode(employeeCode);
// UserDTO userDTO = new UserDTO();
// //写死,新加入到部门表的全部归类到其他部门里面,不按照浙政钉的部门
// userDTO.setDeptId(1865990541445263361L);
// userDTO.setUsername(zzdUserDTO.getUsername());
// userDTO.setName(zzdUserDTO.getUsername());
// userDTO.setNickname(zzdUserDTO.getUsername());
// //浙里办用户
// userDTO.setSystemType("1");
// //取上面的手机号
// userDTO.setPhone(mobile);
// userDTO.setDingtalkEmployeeCode(employeeCode);
// //随便写死给个密码
// userDTO.setPassword("pj"+mobile);
// if(!CollectionUtils.isEmpty(thirdPartyUserEntityList)){
// //todo 这一步现在是没用的,这个最终要考虑是否通过浙政钉里面的人员来限制移动端的使用用户来决定
// //第三方数据库里面有
// //这里只在第三方库里查到,所以要对系统用户表进行新增当前用户,插入一条信息进去
// R<Boolean> booleanR = remoteUserService.registerUser(userDTO);
// //通过唯一手机号查询到刚注册的用户信息,生成token
// R<List<SysUser>> userByMobile=remoteUserService.getUserByMobile(mobile);
// SysUser sysUser = userByMobile.getData().get(0);
// UserTokenDTO userTokenDTO = new UserTokenDTO();
// userTokenDTO.setSysUser(sysUser);
// //登录成功,根据新注册的用户信息返回token
// r=remoteTokenService.generateToken(userTokenDTO);
// }else {
// return R.failed("没有找到当前用户信息,无法访问!");
// }
return R.failed("系统中没有当前用户信息,请联系管理员进行申请!");
}
//两个表都没查到人员不能够使用移动端的系统
// log.info("用户不存在,尝试创建用户");
// R<Boolean> booleanR = remoteUserService.registerUser(userDTO);
// //通过唯一手机号查询到刚注册的用户信息,生成token
//
// R<List<SysUser>> userByMobile=remoteUserService.getUserByMobile(mobile);
// SysUser sysUser = userByMobile.getData().get(0);
//// int i=8/0;
// UserTokenDTO userTokenDTO = new UserTokenDTO();
// userTokenDTO.setSysUser(sysUser);
// //登录成功,根据新注册的用户信息返回token
// R<Map> r = remoteTokenService.generateToken(userTokenDTO);
HashMap<String, Object> objectObjectHashMap = new HashMap<>();
Object accessToken = r.getData().get("accessToken");
Object refreshToken = r.getData().get("refreshToken");
objectObjectHashMap.put("refreshToken",refreshToken);
objectObjectHashMap.put("accessToken",accessToken);
return R.ok(objectObjectHashMap);
}
/**
* 诗画sdk获取参数
*
* @param
* @return
*/
@PostMapping("/shjnsso")
@Inner(value = false)
@Operation(summary = "诗画江南单点登录")
@SysLog("诗画江南单点登录")
@Transactional
@GlobalTransactional
public R shjnsso(@RequestBody @Valid SHJNSSODTO shjnsso,HttpServletRequest httpServletRequest) {
//检查设备指纹
ZZDUserDTO zzdUserDTO = thirdPartyOAuth2Service.shjnsso(shjnsso.getAccountId(), shjnsso.getSessionId());
if (null==zzdUserDTO){
throw new RuntimeException("");
}
ZZDUserDTO.Ext ext = zzdUserDTO.getExt();
String mobile = zzdUserDTO.getMobile();
R<List<SysUser>> userByMobileOrEmployeeCode = remoteUserService.getUserByMobileOrEmployeeCode(null, mobile);
List<SysUser> sysUserList = userByMobileOrEmployeeCode.getData();
if(!CollectionUtils.isEmpty(sysUserList)){
SysUser sysUser = sysUserList.get(0);
//判断当前用户设备是否信任
String md5 = DeviceFingerprintUtil.generate(httpServletRequest);
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(sysUser.getPhone(), md5);
UserTrustedDeviceEntity userTrustedDevice = userTrustedDeviceR.getData();
if (userTrustedDevice == null || userTrustedDevice.getExpireTime().isBefore(LocalDateTime.now())){
return R.failed(-1,"请先信任设备");
}
//登录成功生成token
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUser);
return remoteTokenService.generateToken(userTokenDTO);
}else {
//自动注册
// UserDTO userDTO = new UserDTO();
//// userDTO.setDeptId(1865990541445263361L);
// userDTO.setName(zzdUserDTO.getUsername());
// userDTO.setNickname(zzdUserDTO.getUsername());
// userDTO.setSystemType("2");
// userDTO.setPhone(mobile);
// userDTO.setCreateBy("诗画自动创建");
// //赋予随机密码
// userDTO.setPassword("pj*"+ UUID.randomUUID());
// R<Boolean> booleanR = remoteUserService.registerUser(userDTO);
// R<List<SysUser>> userByMobile=remoteUserService.getUserByMobile(mobile);
// SysUser sysUser = userByMobile.getData().get(0);
// UserTokenDTO userTokenDTO = new UserTokenDTO();
// userTokenDTO.setSysUser(sysUser);
//// //登录成功,根据新注册的用户信息返回token
// r=remoteTokenService.generateToken(userTokenDTO);
return R.failed("系统中没有当前用户信息,请联系管理员进行申请!");
}
}
@Inner(value = false)
@GetMapping("/getAuthToken/{token}")
public R info(@PathVariable(name = "token") String token) {
//调用第三方获取基础信息与数据库进行匹配
// 拼接请求参数
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setUsername("admin"); // 只需要用户名,就可以生成 token
userTokenDTO.setAuthorities(List.of("sys_user_add")); // 权限列表的字符串,能调用的接口范围
// feign 调用 auth 模块生成 token
R<Map> authenticationTokenR = remoteTokenService.generateToken(userTokenDTO);
// // 客户端信息
// RegisteredClient registeredClient = data.getRegisteredClient();
Object accessToken = authenticationTokenR.getData().get("accessToken");
Object refreshToken = authenticationTokenR.getData().get("refreshToken");
Map<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("accessToken", accessToken);
stringObjectHashMap.put("refreshToken", refreshToken);
return R.ok(stringObjectHashMap);
}
@PostMapping("/verify-device")
@SysLog("设备验证")
@Inner(value = false)
public R verifyDevice(@RequestBody DeviceVerifyDTO dto,HttpServletRequest httpServletRequest) {
// 1. 校验验证码
String redisKey = "smsCode_" + dto.getMobilePhone();
String storedCode = (String) redisTemplate.opsForValue().get(redisKey);
if (!dto.getCode().equals(storedCode)) {
return R.failed("验证码错误");
}
// 验证成功后移除验证码(可选)
redisTemplate.delete(redisKey);
// 2. 记录信任设备
//查询之前的是否有过期的数据,有就更新
String generate = DeviceFingerprintUtil.generate(httpServletRequest);
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(dto.getMobilePhone(), generate);
if (userTrustedDeviceR.getData() != null){
userTrustedDeviceR.getData().setExpireTime(LocalDateTime.now().plusMonths(3));
remoteUserTrustedDeviceService.updateById(userTrustedDeviceR.getData());
}else {
UserTrustedDeviceEntity userTrustedDevice = new UserTrustedDeviceEntity();
userTrustedDevice.setMobilePhone(dto.getMobilePhone());
userTrustedDevice.setExpireTime(LocalDateTime.now().plusMonths(3));
userTrustedDevice.setDeviceName("诗画设备认证");
userTrustedDevice.setDeviceFingerprint(generate);
remoteUserTrustedDeviceService.save(userTrustedDevice);
}
return R.ok();
}
/**
* 诗画浦江端
* @param mobilePhone
* @param httpServletRequest
* @param type 短信区分标识 if null 诗画登录短信 1 访客登录短信
* @return
*/
@PostMapping("/loginSms")
@Operation(summary = "发送登录短信")
@SysLog("发送登录短信")
@Inner(value = false)
public R loginSms(@RequestParam String mobilePhone,HttpServletRequest httpServletRequest,
@RequestParam(value = "type",required = false)Integer type) {
Map<String, String> stringHashMap = new LinkedHashMap<>();
String s = SmsCodeUtil.generateNumberCode(6);
stringHashMap.put("code",s);
stringHashMap.put("date","5");
smsService.sendSms(mobilePhone, "20657",stringHashMap, WebUtils.getIP());
// null 适配之前诗画的,=1访客,先else
if (null==type){
redisTemplate.opsForValue().set(CacheConstants.SMS_CODE_KEY +mobilePhone,s, 5, TimeUnit.MINUTES);
}else {
stringRedisTemplate.opsForValue().set(CacheConstants.SMS_VISITOR_CODE_KEY +mobilePhone,s, 5, TimeUnit.MINUTES);
}
// redisTemplate.opsForValue().set(CacheConstants.SMS_CODE_KEY +mobilePhone,s, 5, TimeUnit.MINUTES);
return R.ok();
}
@Inner(value = false)
@GetMapping("/getAuthToken/{token}")
public R info(@PathVariable(name = "token") String token) {
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setUsername("admin");
userTokenDTO.setAuthorities(List.of("sys_user_add"));
R<Map> authenticationTokenR = remoteTokenService.generateToken(userTokenDTO);
Object accessToken = authenticationTokenR.getData().get("accessToken");
Object refreshToken = authenticationTokenR.getData().get("refreshToken");
Map<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("accessToken", accessToken);
stringObjectHashMap.put("refreshToken", refreshToken);
return R.ok(stringObjectHashMap);
}
@PostMapping("/verify-device")
@SysLog("设备验证")
@Inner(value = false)
public R verifyDevice(@RequestBody DeviceVerifyDTO dto, HttpServletRequest httpServletRequest) {
String redisKey = "smsCode_" + dto.getMobilePhone();
String storedCode = (String) redisTemplate.opsForValue().get(redisKey);
if (!dto.getCode().equals(storedCode)) {
return R.failed("验证码错误");
}
redisTemplate.delete(redisKey);
String generate = DeviceFingerprintUtil.generate(httpServletRequest);
R<UserTrustedDeviceEntity> userTrustedDeviceR =
remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(dto.getMobilePhone(), generate);
if (userTrustedDeviceR.getData() != null) {
userTrustedDeviceR.getData().setExpireTime(LocalDateTime.now().plusMonths(3));
remoteUserTrustedDeviceService.updateById(userTrustedDeviceR.getData());
}
else {
UserTrustedDeviceEntity userTrustedDevice = new UserTrustedDeviceEntity();
userTrustedDevice.setMobilePhone(dto.getMobilePhone());
userTrustedDevice.setExpireTime(LocalDateTime.now().plusMonths(3));
userTrustedDevice.setDeviceName("设备认证");
userTrustedDevice.setDeviceFingerprint(generate);
remoteUserTrustedDeviceService.save(userTrustedDevice);
}
return R.ok();
}
/**
* @param type 短信区分:null 登录短信;1 访客登录短信
*/
@PostMapping("/loginSms")
@Operation(summary = "发送登录短信")
@SysLog("发送登录短信")
@Inner(value = false)
public R loginSms(@RequestParam String mobilePhone, HttpServletRequest httpServletRequest,
@RequestParam(value = "type", required = false) Integer type) {
Map<String, String> stringHashMap = new LinkedHashMap<>();
String s = SmsCodeUtil.generateNumberCode(6);
stringHashMap.put("code", s);
stringHashMap.put("date", "5");
smsService.sendSms(mobilePhone, "20657", stringHashMap, WebUtils.getIP());
if (null == type) {
redisTemplate.opsForValue().set(CacheConstants.SMS_CODE_KEY + mobilePhone, s, 5, TimeUnit.MINUTES);
}
else {
stringRedisTemplate.opsForValue()
.set(CacheConstants.SMS_VISITOR_CODE_KEY + mobilePhone, s, 5, TimeUnit.MINUTES);
}
return R.ok();
}
}
@@ -1,31 +1,19 @@
package com.bsd.pjcloud.thirdparty.service;
import com.alibaba.fastjson.JSON;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.thirdparty.config.OAuth2ClientConfig;
import com.bsd.pjcloud.thirdparty.constant.UserTypeEnum;
import com.bsd.pjcloud.thirdparty.dto.*;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyDeptEntity;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyLogEntity;
import com.bsd.pjcloud.thirdparty.entity.ThirdPartyUserEntity;
import com.bsd.pjcloud.thirdparty.exception.ThirdPartyException;
import com.bsd.pjcloud.thirdparty.shpj.ShpjOpenapiUser;
import com.bsd.pjcloud.thirdparty.shpj.ShpjUserClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import java.util.*;
@@ -49,14 +37,10 @@ public class ThirdPartyOAuth2Service {
private final int MAX_PAGE_SIZE = 100; // 每次最多获取 100 条
private final ThirdPartyDeptService partyDeptService;
private final ThirdPartyDeptService partyDeptService;
private final ThirdPartyUserService partyUserService;
private final ThirdPartyLogService thirdPartyLogService;
private final ShpjUserClient shpjUserClient;
public TokenDTO getAccessToken() {
TokenDTO cachedToken = tokenCache.get("access_token");
@@ -357,121 +341,4 @@ public class ThirdPartyOAuth2Service {
return response.getBody();
}
/**
* 获取浙政钉的token
* @param token
* @return
*/
public ZZDUserDTO zzdsso(String token) {
// String zzdToken = getZZDToken(token);
//todo 根据是诗画还是浙政钉分别去获取不同的token
//使用token获取当前登录人的信息(浙政钉和诗画的在一起)
// ZZDUserDTO zzdUser = getZZDUserInfo(zzdToken);
ZZDUserDTO zzdUser = getZZDUserInfo(token,UserTypeEnum.ZZD.getType());
return zzdUser;
}
public String getZZDToken(String token) {
String url = baseUrl+config.getZzdTokenUrl()+token;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
ResponseEntity<ZZDResponse> response = restTemplate.exchange(
url, HttpMethod.POST, requestEntity, ZZDResponse.class
);
String zzdToken = response.getBody().getData();
return zzdToken;
}
/**
* 浙证钉和诗画工用
* @param zzdToken
* @return
*/
public ZZDUserDTO getZZDUserInfo(String zzdToken,String type) {
// 构建请求URL
try {
String url = baseUrl + config.getZzdShUserInfoUrl()+"?token=" + zzdToken;
// 获取 Bearer Token
String accessToken = getAccessToken().getAccess_token();
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
// 构建请求实体
// 发送请求
ParameterizedTypeReference<ApiResponse<ZZDUserDTO>> responseType = new ParameterizedTypeReference<ApiResponse<ZZDUserDTO>>() {};
ResponseEntity<ApiResponse<ZZDUserDTO>> response = restTemplate.exchange(
url, HttpMethod.GET, requestEntity, responseType
);
// if (true){
if (response.getBody().getCode().equals("-1")){
log.error("获取浙政钉/诗画用户信息失败");
log.error("token",zzdToken);
log.error("response",JSON.toJSONString(response.getBody()));
ThirdPartyLogEntity thirdPartyLogEntity = new ThirdPartyLogEntity();
if (type.equals(UserTypeEnum.ZZD.getType())){
thirdPartyLogEntity.setApiName(UserTypeEnum.ZZD.getDescription());
}else if (type.equals(UserTypeEnum.SH.getType())){
thirdPartyLogEntity.setApiName(UserTypeEnum.SH.getDescription());
}
// thirdPartyLogEntity.setApiName("获取浙政钉/诗画用户信息失败");
thirdPartyLogEntity.setRequestUrl( baseUrl + config.getZzdShUserInfoUrl());
thirdPartyLogEntity.setRequestParameter(zzdToken);
thirdPartyLogEntity.setResponseBody(JSON.toJSONString(response.getBody()));
thirdPartyLogEntity.setErrorCode(response.getBody().getCode());
thirdPartyLogEntity.setErrorMsg(response.getBody().getDescription());
thirdPartyLogEntity.setHttpStatus(response.getStatusCode().toString());
thirdPartyLogEntity.setCreateBy("thirdPartylog");
thirdPartyLogService.saveLog(thirdPartyLogEntity);
throw new RuntimeException(response.getBody().getDescription());
}
return response.getBody().getData();
} catch (HttpStatusCodeException e) {
log.error("第三方接口HTTP异常 状态码:{} 响应:{}", e.getRawStatusCode(), e.getResponseBodyAsString());
throw new ThirdPartyException("第三方服务异常,请联系管理员", "THIRD_PARTY_500");
} catch (ResourceAccessException e) {
log.error("第三方接口网络异常", e);
throw new ThirdPartyException("第三方服务暂时不可用,请稍后重试", "NETWORK_ERROR");
}
}
/**
* 诗画浦江单点:直连诗画开放接口获取用户,不再经对方 /login/shpj 中转
*/
public ZZDUserDTO shjnsso(String accountId, String sessionId) {
ShpjOpenapiUser openapiUser = shpjUserClient.getUserInfo(accountId, sessionId);
if (openapiUser == null || openapiUser.getIsLogin() != 1) {
throw new ThirdPartyException("请先登录诗画客户端", "SHPJ_NOT_LOGIN");
}
if (!StringUtils.hasText(openapiUser.getMobile())) {
throw new ThirdPartyException("诗画用户手机号为空", "SHPJ_MOBILE_EMPTY");
}
return mapShpjOpenapiUserToZzdDto(openapiUser);
}
private static ZZDUserDTO mapShpjOpenapiUserToZzdDto(ShpjOpenapiUser user) {
ZZDUserDTO dto = new ZZDUserDTO();
dto.setUsername(user.getNickName());
dto.setNickName(user.getNickName());
dto.setMobile(user.getMobile());
ZZDUserDTO.Ext ext = dto.new Ext();
ext.setNickNameCn(user.getNickName());
ext.setMobile(user.getMobile());
ext.setHeadImgUrl(user.getHeadImgUrl());
dto.setExt(ext);
return dto;
}
}
@@ -1,17 +0,0 @@
package com.bsd.pjcloud.thirdparty.shpj;
import lombok.Data;
@Data
public class ShpjApiWrapper<T> {
private int code;
private T data;
private String message;
public boolean isSuccessful() {
return code == 0;
}
}
@@ -1,42 +0,0 @@
package com.bsd.pjcloud.thirdparty.shpj;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* 诗画浦江开放接口用户信息(对齐对方 OpenapiUser)
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ShpjOpenapiUser {
private int is_faceVerify;
private String city;
private String nickName;
private boolean forbidden;
private String mobile;
private String deviceId;
private boolean freezed;
private String accountId;
private int isLogin;
private String createdAt;
private String headImgUrl;
private String clientCode;
private String recentActiveTime;
private int grade;
private String refCode;
}
@@ -1,24 +0,0 @@
package com.bsd.pjcloud.thirdparty.shpj;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 诗画浦江开放接口配置(直连 vcms,不经对方中转服务)
*/
@Data
@Component
@ConfigurationProperties(prefix = "pjcloud.shpj")
public class ShpjProperties {
private String appKey;
private String appSecret;
private String connectNo;
private String baseUrl = "https://vcms.tmuyun.com";
private int httpTimeoutSeconds = 5;
}
@@ -1,38 +0,0 @@
package com.bsd.pjcloud.thirdparty.shpj;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* 诗画浦江签名:timestamp&&appkey&&appsecret&&noncestr&&paramValues
*/
public final class ShpjSignHelper {
private ShpjSignHelper() {
}
public static String sign(Map<String, String> businessParams, String timestamp, String appKey, String appSecret, String nonce) {
String raw = String.format("%s&&%s&&%s&&%s", timestamp, appKey, appSecret, nonce) + extraSign(businessParams);
return DigestUtils.md5DigestAsHex(raw.getBytes(StandardCharsets.UTF_8));
}
private static String extraSign(Map<String, String> paramsMap) {
SortedMap<String, String> resMap = new TreeMap<>(paramsMap);
String[] keys = resMap.keySet().toArray(new String[0]);
Arrays.sort(keys);
StringBuilder paramBuffer = new StringBuilder();
for (String key : keys) {
String value = resMap.get(key);
if (StringUtils.hasLength(value) && !"0".equals(value)) {
paramBuffer.append("&&").append(value);
}
}
return paramBuffer.toString();
}
}
@@ -1,85 +0,0 @@
package com.bsd.pjcloud.thirdparty.shpj;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.bsd.pjcloud.thirdparty.exception.ThirdPartyException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 诗画浦江用户开放接口客户端(直连)
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class ShpjUserClient {
private final ShpjProperties properties;
private final ObjectMapper objectMapper;
/**
* 按 accountId + sessionId 查询诗画用户详情
*/
public ShpjOpenapiUser getUserInfo(String accountId, String sessionId) {
if (!StringUtils.hasText(accountId) || "-1".equals(accountId)) {
throw new ThirdPartyException("请先登录诗画客户端", "SHPJ_NOT_LOGIN");
}
if (!StringUtils.hasText(sessionId)) {
throw new ThirdPartyException("sessionId不能为空", "SHPJ_SESSION_EMPTY");
}
if (!StringUtils.hasText(properties.getAppKey()) || !StringUtils.hasText(properties.getAppSecret())
|| !StringUtils.hasText(properties.getConnectNo())) {
throw new ThirdPartyException("诗画浦江开放接口未配置", "SHPJ_CFG");
}
String timestamp = String.valueOf(System.currentTimeMillis());
String nonce = timestamp + "shpj";
Map<String, String> businessParams = new HashMap<>(2);
businessParams.put("sessionId", sessionId);
businessParams.put("connectNo", properties.getConnectNo());
Map<String, Object> form = new HashMap<>();
form.put("appkey", properties.getAppKey());
form.put("noncestr", nonce);
form.put("timestamp", timestamp);
form.put("signature", ShpjSignHelper.sign(businessParams, timestamp, properties.getAppKey(), properties.getAppSecret(), nonce));
form.putAll(businessParams);
String apiUrl = "/openapi/v2/" + accountId + "/detail";
String fullUrl = properties.getBaseUrl() + apiUrl;
log.info("调用诗画浦江接口[{}]开始, accountId={}, sessionId={}", apiUrl, accountId, sessionId);
try (HttpResponse httpResponse = HttpRequest.get(fullUrl)
.timeout(properties.getHttpTimeoutSeconds() * 1000)
.form(form)
.execute()) {
String body = httpResponse.body();
if (!httpResponse.isOk()) {
log.error("诗画浦江接口[{}]HTTP错误:{} - {}", apiUrl, httpResponse.getStatus(), body);
throw new ThirdPartyException("诗画浦江接口调用失败", "SHPJ_HTTP");
}
log.info("诗画浦江接口[{}]完成, 返回:{}", apiUrl, body);
ShpjApiWrapper<ShpjOpenapiUser> resp = objectMapper.readValue(body,
objectMapper.getTypeFactory().constructParametricType(ShpjApiWrapper.class, ShpjOpenapiUser.class));
if (!resp.isSuccessful()) {
log.error("诗画浦江接口[{}]业务失败: [{}-{}]", apiUrl, resp.getCode(), resp.getMessage());
throw new ThirdPartyException("诗画浦江接口调用失败:" + resp.getMessage(), "SHPJ_BIZ_" + resp.getCode());
}
return resp.getData();
} catch (ThirdPartyException e) {
throw e;
} catch (Exception e) {
log.error("诗画浦江接口[{}]异常", apiUrl, e);
throw new ThirdPartyException("诗画浦江服务暂时不可用", "SHPJ_ERROR");
}
}
}
@@ -34,19 +34,6 @@ oauth2:
dept-url: /prod-api/oauth2/apis/orguser/v1/list_sub_depts
dept-users-url: /prod-api/oauth2/apis/orguser/v1/list_dept_users
user_detail-url: /prod-api/oauth2/apis/orguser/v1/user_detail
# 数字政务单点接口
zzd_token_url: /prod-api/login/szzw/
# 浙证钉和诗画公用的获取用户信息接口(诗画已改为本系统直连 vcms)
zzd_sh_user_info_url: /prod-api/oauth2/apis/sso/v1/fetch_user_by_uabase_token
# 诗画浦江开放接口(直连 https://vcms.tmuyun.com,不经对方 /login/shpj)
pjcloud:
shpj:
app-key: ${SHPJ_APP_KEY:rQZzIvB7beaeZBf2IbQnemYv}
app-secret: ${SHPJ_APP_SECRET:4d248cd64bc5653fd2c1d6bb425b2817}
connect-no: ${SHPJ_CONNECT_NO:afb7e8fbf5f4eb9f}
base-url: ${SHPJ_BASE_URL:https://vcms.tmuyun.com}
http-timeout-seconds: 5
# 正元智慧
# https://gwyc.zj.gov.cn/API/%E7%9C%81%E5%B9%B3%E5%8F%B0API%E5%AF%B9%E6%8E%A5%E6%8E%A5%E5%8F%A3.html
@@ -263,9 +263,9 @@ public class SysUser extends Model<SysUser> implements Serializable {
private String email;
/**
* 员工浙政钉编码
* 员工钉钉编码
*/
@Schema(description = "员工浙政钉编码")
@Schema(description = "员工钉钉编码")
private String dingtalkEmployeeCode;
/**
@@ -256,7 +256,7 @@ public class UserVO implements BaseFacesInterface, BaseUserIdentityInterface, Se
@Schema(description = "邮箱")
private String email;
@Schema(description = "员工浙政钉编码")
@Schema(description = "员工钉钉编码")
private String dingtalkEmployeeCode;
/**
@@ -811,10 +811,10 @@ public class SysUserController {
}
/**
* 根据浙政钉号和手机号查询
* 根据员工钉钉编码和手机号查询
*
* @param employeeCode
* @param mobile
* @param employeeCode 员工钉钉编码(可空)
* @param mobile 手机号
* @return
*/
@GetMapping("/getUserByMobileOrEmployeeCode")