HouseController.java 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. package com.fdkanfang.web.backend;
  2. import cn.hutool.core.io.FileUtil;
  3. import com.alibaba.fastjson.JSON;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.fdkanfang.common.constant.ConstantFilePath;
  6. import com.fdkanfang.common.constant.MsgCode;
  7. import com.fdkanfang.common.constant.TypeCode;
  8. import com.fdkanfang.common.enums.ImageResolutionRate;
  9. import com.fdkanfang.common.enums.ResultCodeEnum;
  10. import com.fdkanfang.common.exception.BaseRuntimeException;
  11. import com.fdkanfang.common.exception.CommonBaseException;
  12. import com.fdkanfang.common.model.PageDto;
  13. import com.fdkanfang.common.util.*;
  14. import com.fdkanfang.domain.backend.*;
  15. import com.fdkanfang.domain.dto.*;
  16. import com.fdkanfang.domain.response.HouseResponse;
  17. import com.fdkanfang.domain.response.UserResponse;
  18. import com.fdkanfang.service.backend.*;
  19. import com.fdkanfang.web.backend.utils.UserUtils;
  20. import com.fdkanfang.web.mq.config.RabbitConfig;
  21. import com.github.pagehelper.PageInfo;
  22. import io.swagger.annotations.*;
  23. import lombok.extern.log4j.Log4j2;
  24. import org.apache.commons.lang3.StringUtils;
  25. import org.apache.shiro.authz.annotation.Logical;
  26. import org.apache.shiro.authz.annotation.RequiresRoles;
  27. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  28. import org.springframework.beans.BeanUtils;
  29. import org.springframework.beans.factory.annotation.Autowired;
  30. import org.springframework.beans.factory.annotation.Value;
  31. import org.springframework.transaction.annotation.Transactional;
  32. import org.springframework.util.CollectionUtils;
  33. import org.springframework.web.bind.annotation.*;
  34. import org.springframework.web.multipart.MultipartFile;
  35. import javax.validation.Valid;
  36. import java.io.File;
  37. import java.io.IOException;
  38. import java.util.Date;
  39. import java.util.HashMap;
  40. import java.util.List;
  41. import java.util.Map;
  42. /**
  43. * Created by owen on 2020/2/18 0018 12:17
  44. */
  45. @Api(tags = "房源管理")
  46. @RestController
  47. @RequestMapping("manage/house")
  48. @Transactional
  49. @Log4j2
  50. public class HouseController extends BaseController {
  51. // 服务器文件保存位置
  52. @Value("${output.file.path}")
  53. private String OUTPATH;
  54. @Value("${server.domain}")
  55. private String domain;
  56. @Value("${4dkankan.host}")
  57. private String KankanHost;
  58. @Autowired
  59. private HouseService2 houseService2;
  60. @Autowired
  61. private ImageService2 imageService2;
  62. @Autowired
  63. private RabbitTemplate rabbitTemplate;
  64. @Autowired
  65. private ISceneProService sceneProService;
  66. @Autowired
  67. private ISceneProEditService sceneProEditService;
  68. @Autowired
  69. private RoleService2 roleService;
  70. @Autowired
  71. private UserUtils userUtils;
  72. @ApiOperation("房源列表")
  73. @PostMapping("list")
  74. public R list(@RequestBody PageDto param){
  75. String token = getToken();
  76. // 获取用户角色
  77. List<String> userRoles = userUtils.getRolesByToken(token);
  78. UserEntity user = userUtils.getUserByToken(token);
  79. Long userId = user.getId();
  80. PageInfo<HouseResponse> page = null;
  81. if(CollectionUtils.isEmpty(userRoles)){
  82. log.info("用户权限为空,无法获取房源列表");
  83. throw new CommonBaseException(ResultCodeEnum.D101 , "权限不足");
  84. }
  85. if(userRoles.size() == 3){
  86. //管理员获取全部房源列表
  87. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, null, null));
  88. }else if(userRoles.size() == 2){
  89. if(userRoles.contains("edit") && userRoles.contains("upload")){
  90. //同时拥有编辑和上传权限
  91. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, userId ,userId));
  92. }else if(userRoles.contains("edit") && !userRoles.contains("upload")){
  93. //只拥有编辑权限
  94. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, userId ,null));
  95. }else if (!userRoles.contains("edit") && userRoles.contains("upload")){
  96. //只拥有上传权限
  97. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, null ,userId));
  98. }else{
  99. //理论上不存在此分支情况
  100. log.error("上传的权限超出了当前有效权限范围");
  101. throw new CommonBaseException(ResultCodeEnum.D101 , "存在无效权限");
  102. }
  103. }else if(userRoles.size() == 1){
  104. if(userRoles.contains("edit")){
  105. //只拥有编辑权限
  106. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, userId ,null));
  107. }else if (userRoles.contains("upload")){
  108. //只拥有上传权限
  109. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, null ,userId));
  110. }else{
  111. //只有管理用户的权限,理论不应该单独给这种角色
  112. log.warn("用户[{}]只单独给了用户管理权限" ,userId);
  113. throw new CommonBaseException(ResultCodeEnum.D101 , "当前权限不能查看数据");
  114. }
  115. }else{
  116. log.info("用户[{}]的权限超过3个,默认给拉取所有房源");
  117. //默认获取全部
  118. page = new PageInfo<>(houseService2.findBySearchKeyForEditOrUploader(param, null, null));
  119. }
  120. return new R(2000, page);
  121. }
  122. /**
  123. * 服务器版本+垂直校验
  124. *
  125. * 使用mq队列
  126. *
  127. * 上传文件,不能使用json传参,只能用表单
  128. *
  129. * 如果图片审核不通过,就直接把这条记录禁用or删除,从新添加一条。
  130. * 修改只能修改房源信息,不能修改图片
  131. *
  132. * 上传的图片必须是以xxx_xxx.jpg 格式命名,xxx_:图片所属的户型
  133. *
  134. *
  135. * 这里要用手写的表单验证验证方式
  136. *
  137. * 提前把垂直校验,模型图片地址写入数据库,需要配合状态来判断算法计算是否完成;
  138. */
  139. @RequiresRoles(value = {"admin", "edit", "upload"}, logical = Logical.OR)
  140. @ApiOperation("新增或修改房源信息 + 垂直校验")
  141. @PostMapping(value = "save", consumes = {"multipart/form-data"})
  142. @Transactional(rollbackFor = Exception.class)
  143. public R save(HouseDto param){
  144. if(StringUtils.isBlank(param.getDistrictName())){
  145. log.error("小区名称不能为空");
  146. return new R(MsgCode.e_COMMON_3001, "小区名称不能为空");
  147. }
  148. if(null == param.getFloor() || !StringUtils.isNoneBlank(param.getUnitType() , param.getArea() , param.getOrientation())){
  149. return new R(MsgCode.e_COMMON_3001, "楼层/户型/面积/朝向不能为空");
  150. }
  151. // 处理上传图片
  152. MultipartFile[] files = param.getFiles();
  153. if (files == null || files.length <= 0) {
  154. log.info("文件为空");
  155. return new R(MsgCode.e_COMMON_3100, MsgCode.msg_COMMON_3100);
  156. }
  157. boolean isNewAdd = false;
  158. HouseEntity house = null;
  159. if (param.getId() == null) {
  160. isNewAdd = true;
  161. }
  162. house = updateOrInsertHouse(param);
  163. // 只有新增是才保存图片,修改时另外处理
  164. if (isNewAdd) {
  165. //场景码
  166. String sceneCode = house.getSceneCode();
  167. boolean needSendMqMsg = false;
  168. String directoryName = OUTPATH + sceneCode + File.separator + "input_img"+ File.separator;
  169. FileUtil.mkdir(directoryName);
  170. HashMap<String, String> ossVerticalImageHighMap = new HashMap<>();
  171. HashMap<String, String> ossVerticalImageLowMap = new HashMap<>();
  172. String imageHighPath = null;
  173. String imageLowPath = null;
  174. ImageResolutionRate maxResolutionRate = null;
  175. for (MultipartFile file : files) {
  176. String filename = file.getOriginalFilename();
  177. filename = checkAndChangeFileName(filename);
  178. imageHighPath = OUTPATH + sceneCode + "/output_img/" + filename;
  179. imageLowPath = OUTPATH + sceneCode + "/output_img_low/" + filename;
  180. // 提前把算法的图片路径存到数据库,但不代表算法运行成功,最后好是要看house的状态;
  181. String ossVerticalHighPath = ConstantFilePath.OSS_IMAGE_PATH+ sceneCode+"/pan/high/"+ filename;
  182. String ossVerticalLowPath = ConstantFilePath.OSS_IMAGE_PATH+ sceneCode+"/pan/low/"+ filename;
  183. // 封装垂直校验后的图片到信息到oss
  184. ossVerticalImageHighMap.put(imageHighPath, ossVerticalHighPath);
  185. ossVerticalImageLowMap.put(imageLowPath, ossVerticalLowPath);
  186. try {
  187. ImageResolutionRate tmpResolutionRate = addAndGetResolutionRate(file , directoryName , ossVerticalHighPath , house.getId());
  188. maxResolutionRate = tmpResolutionRate;
  189. needSendMqMsg = true;
  190. } catch (IOException e) {
  191. needSendMqMsg = false;
  192. log.error("保存照片到本地和持久化image对象出现异常:{}" , e);
  193. }
  194. }
  195. insertScene(maxResolutionRate , house);
  196. if (needSendMqMsg) {
  197. HashMap<Object, Object> mqMap = new HashMap<>();
  198. mqMap.put("ossImageHigh", ossVerticalImageHighMap);
  199. mqMap.put("ossImageLow", ossVerticalImageLowMap);
  200. mqMap.put("id", house.getId());
  201. mqMap.put("basePath", OUTPATH);
  202. mqMap.put("dataDescribe", "");
  203. log.info("houseId: {}", house.getId());
  204. //发消息到mq
  205. rabbitTemplate.convertAndSend(RabbitConfig.VERTICAL_EXCHANGE, RabbitConfig.VERTICAL_QUEUE_ROUTING, mqMap);
  206. }
  207. }
  208. return new R(MsgCode.SUCCESS_CODE, house);
  209. }
  210. @Transactional(rollbackFor = Exception.class)
  211. public void insertScene(ImageResolutionRate maxResolutionRate , HouseEntity house){
  212. SceneProEntity sceneProEntity = new SceneProEntity();
  213. SceneProEditEntity sceneProEditEntity = new SceneProEditEntity();
  214. if(ImageResolutionRate.TWO_K.equals(maxResolutionRate)){
  215. //2k
  216. sceneProEntity.setSceneScheme(11);
  217. }else{
  218. //4k
  219. sceneProEntity.setSceneScheme(10);
  220. }
  221. sceneProEntity.setNum(house.getSceneCode());
  222. //TODO:不知道为何数据库设置的默认不生效
  223. sceneProEditEntity.setM2dVisi(1);
  224. sceneProEditEntity.setM3dVisi(1);
  225. sceneProEditEntity.setPanoVisi(1);
  226. sceneProEditEntity.setMapVisi(1);
  227. int insertOne = sceneProService.save(sceneProEntity);
  228. if(insertOne != 1){
  229. throw new CommonBaseException(ResultCodeEnum.D101, "新增scenePro记录失败");
  230. }
  231. sceneProEditEntity.setProId(sceneProEntity.getId());
  232. int insertTwo = sceneProEditService.save(sceneProEditEntity);
  233. if(insertTwo != 1){
  234. throw new CommonBaseException(ResultCodeEnum.D101, "新增sceneProEdit记录失败");
  235. }
  236. }
  237. @Transactional(rollbackFor = Exception.class)
  238. public ImageResolutionRate addAndGetResolutionRate(MultipartFile file , String directoryName ,
  239. String ossVerticalHighPath , Long houseId) throws IOException {
  240. if(null == file || null == houseId){
  241. log.error("房源id或者文件为空,无法生成image记录");
  242. return null;
  243. }
  244. String filename = file.getOriginalFilename();
  245. filename = checkAndChangeFileName(filename);
  246. // 获取图片房间类型
  247. String imageType = StringUtils.substringBefore(filename, "_");
  248. // 本地图片保存位置: /root/data/kanfang/场景码/input_img/xxxx.jpg
  249. String fileFullPath = directoryName + filename;
  250. ImageEntity image = new ImageEntity();
  251. image.setVerticalPath(ossVerticalHighPath);
  252. image.setFileName(filename);
  253. // 这个参数给前端用,只要有文件名就可以了
  254. image.setPath(fileFullPath);
  255. image.setLocalPath(fileFullPath);
  256. image.setHouseId(houseId);
  257. // 默认所有图片都是一楼
  258. image.setFloor(1);
  259. image.setType(imageType);
  260. //将图片保存到本地指定目录filePath eg: /root/data/kanfang/d_9iRDUgn3l/input_img/xxx.jpg
  261. int resolutionRate = FileUtils.downloanAndGetResolutionRate(file.getInputStream(), fileFullPath , true);
  262. //照片分辨率
  263. ImageResolutionRate maxResolutionRate = ImageResolutionRate.getResolutionRateByRate(resolutionRate);
  264. log.info("照片{}的像素为:{}" , fileFullPath , resolutionRate);
  265. image.setResolutionRate(null != maxResolutionRate ? maxResolutionRate.name() : "TWO_K");
  266. int insert = imageService2.save(image);
  267. if(insert != 1){
  268. throw new CommonBaseException(ResultCodeEnum.D101, "新增image记录失败");
  269. }
  270. return maxResolutionRate;
  271. }
  272. private String checkAndChangeFileName(String filename){
  273. if(StringUtils.isBlank(filename)){
  274. throw new CommonBaseException(ResultCodeEnum.D100 , "文件名不能为空");
  275. }
  276. if(filename.contains(".JPG")){
  277. int index = filename.lastIndexOf(".");
  278. String fileNameWithoutPostfix = filename.substring(0 , index);
  279. filename = fileNameWithoutPostfix + ".jpg";
  280. log.info("照片的格式写成了大写,需要转成小写:{}" , filename);
  281. }
  282. // 检查一下,生成模型的照片名称不能有"-",否则生成模型会出现问题
  283. if (filename.contains("-")) {
  284. log.error("filename: {}", filename);
  285. throw new BaseRuntimeException(MsgCode.e_COMMON_3003, "图片命名不符合要求,不能包含符号-,请使用_");
  286. }
  287. if (RegexpUtils.isContainChinese(filename)) {
  288. log.error("图片名称不允许中文字符: {}", filename);
  289. throw new BaseRuntimeException(MsgCode.e_COMMON_3003, "图片名称不允许中文字符");
  290. }
  291. return filename;
  292. }
  293. @Transactional(rollbackFor = Exception.class)
  294. public HouseEntity updateOrInsertHouse(HouseDto param){
  295. if(null == param){
  296. return null;
  297. }
  298. HouseEntity house = null;
  299. if (param.getId() == null) {
  300. house = new HouseEntity();
  301. UserEntity user = userUtils.getUserByToken(getToken());
  302. house.setUserId(user.getId());
  303. param.setId(null);
  304. BeanUtils.copyProperties(param, house);
  305. Integer byMaxNum = houseService2.findByMaxNum();
  306. // 用于第一条数据
  307. byMaxNum = (byMaxNum == null)? 10000:byMaxNum;
  308. String sceneCode = getSceneCode();
  309. house.setNum(byMaxNum + 1);
  310. house.setSceneCode(sceneCode);
  311. house.setWebSite(domain+"?m="+sceneCode);
  312. house.setFilePath(OUTPATH + sceneCode);
  313. houseService2.save(house);
  314. } else {
  315. house = houseService2.findById(param.getId());
  316. if (house == null) {
  317. log.error("房源不存在: {}", param.getId());
  318. throw new CommonBaseException(ResultCodeEnum.D101 , "房源不存在");
  319. }
  320. BeanUtils.copyProperties(param, house);
  321. house.setUpdateTime(new Date());
  322. houseService2.update(house);
  323. }
  324. return house;
  325. }
  326. @ApiOperation("预审")
  327. @ResponseBody
  328. @PostMapping(value = "/auditHouse")
  329. @Transactional(rollbackFor = Exception.class)
  330. public Result auditHouse(@RequestBody @ApiParam(name = "用户登录注册实体", value = "传入json格式", required = true) HouseAuditDto houseAuditDto) {
  331. if(StringUtils.isBlank(houseAuditDto.getDesc()) || null == houseAuditDto.getHouseId() || null == houseAuditDto.getResult()){
  332. throw new CommonBaseException(ResultCodeEnum.D3001);
  333. }
  334. HouseEntity houseEntity = houseService2.findById(houseAuditDto.getHouseId());
  335. if(null == houseEntity){
  336. throw new CommonBaseException(ResultCodeEnum.D101 , "房源不存在");
  337. }
  338. if(houseAuditDto.getResult().compareTo(1) != 0 && houseAuditDto.getResult().compareTo(0) != 0){
  339. throw new CommonBaseException(ResultCodeEnum.D101 , "审批结果格式不正确");
  340. }
  341. houseEntity.setAuditResult(houseAuditDto.getResult());
  342. houseEntity.setAuditDesc(houseAuditDto.getDesc());
  343. houseEntity.setUpdateTime(new Date());
  344. int update = houseService2.update(houseEntity);
  345. if(update != 1){
  346. throw new CommonBaseException(ResultCodeEnum.D101 , "更新房源预审信息失败");
  347. }
  348. return Result.success();
  349. }
  350. @ApiOperation("标记不能编辑")
  351. @ResponseBody
  352. @PostMapping(value = "/markFail")
  353. public Result markCannotEditHouse(@RequestBody @ApiParam(name = "用户登录注册实体", value = "传入json格式", required = true) HouseAuditDto houseAuditDto) {
  354. if(StringUtils.isBlank(houseAuditDto.getDesc()) || null == houseAuditDto.getHouseId()){
  355. throw new CommonBaseException(ResultCodeEnum.D3001);
  356. }
  357. HouseEntity houseEntity = houseService2.findById(houseAuditDto.getHouseId());
  358. if(null == houseEntity){
  359. throw new CommonBaseException(ResultCodeEnum.D101 , "房源不存在");
  360. }
  361. houseEntity.setCanNotEdit(0);
  362. houseEntity.setCanNotEditDesc(houseAuditDto.getDesc());
  363. houseEntity.setUpdateTime(new Date());
  364. int update = houseService2.update(houseEntity);
  365. if(update != 1){
  366. throw new CommonBaseException(ResultCodeEnum.D101 , "更新房源无法制作信息失败");
  367. }
  368. return Result.success();
  369. }
  370. /**
  371. *
  372. * @param houseId
  373. * @param floor 可选参数
  374. */
  375. @ApiOperation("查询房源信息")
  376. @GetMapping("detail")
  377. @ApiImplicitParams({
  378. @ApiImplicitParam(name = "houseId", value = "房源id", required = true),
  379. @ApiImplicitParam(name = "floor", value = "楼层", required = false)
  380. })
  381. public R detail(Long houseId, Integer floor){
  382. HouseEntity house = houseService2.findById(houseId);
  383. List<ImageEntity> images = imageService2.findByHouseIdAndFloor(houseId, floor);
  384. if(null != house){
  385. house.setImages(images);
  386. return new R(MsgCode.SUCCESS_CODE, house);
  387. }else{
  388. return new R(MsgCode.ERROR_CODE, "房源不存在");
  389. }
  390. }
  391. @RequiresRoles(value = {"admin", "edit", "upload"}, logical = Logical.OR)
  392. @ApiOperation("删除房源(软删除)")
  393. @GetMapping("removes/{ids}")
  394. @Transactional(rollbackFor = Exception.class)
  395. public R removes(@PathVariable String ids){
  396. List<String> userRoles = userUtils.getRolesByToken(getToken());
  397. String roleKey = null;
  398. if (userRoles.size() <= 1) {
  399. // 只有一个权限时
  400. roleKey = (String)userRoles.get(0);
  401. if (!"upload".equals(roleKey)) {
  402. log.warn("只有一个权限的非上传者角色,不能删除房源");
  403. throw new CommonBaseException(ResultCodeEnum.D101 , "权限不足,不能删除房源");
  404. }
  405. }else if(userRoles.size() == 2){
  406. if(userRoles.contains("edit") && userRoles.contains("admin")){
  407. //理论上不会有这种权限组合
  408. log.warn("多于一个权限的角色,只有用户管理和编辑房源权限不能删除房源");
  409. throw new CommonBaseException(ResultCodeEnum.D101 , "权限不足(缺少上传房源权限),不能删除房源");
  410. }
  411. }
  412. String[] split = ids.split(",");
  413. for (String s : split) {
  414. HouseEntity houseEntity = houseService2.findById(Long.valueOf(s));
  415. if (houseEntity == null) {
  416. log.error("房源id有误,没有存在的房源");
  417. return new R(MsgCode.e_COMMON_3002,"没有存在的房源");
  418. }
  419. if(null != houseEntity.getStatus()){
  420. if((houseEntity.getStatus().compareTo(0) == 0) && (null != houseEntity.getStatus() && houseEntity.getStatus().compareTo(1) == 0)){
  421. //预审通过了的,还未垂直校验的,不能删除
  422. log.error("房源垂直校验计算中不能删除");
  423. throw new CommonBaseException(ResultCodeEnum.D101 , "房源垂直校验计算中不能删除");
  424. }
  425. if(houseEntity.getStatus().compareTo(3) == 0){
  426. log.error("房源生成模型计算中不能删除");
  427. throw new CommonBaseException(ResultCodeEnum.D101 , "房源生成模型计算中不能删除");
  428. }
  429. }
  430. houseEntity.setRecStatus("I");
  431. houseEntity.setUpdateTime(new Date());
  432. int update = houseService2.update(houseEntity);
  433. if(update != 1){
  434. log.error( "删除房源失败[{}]" , houseEntity.getId());
  435. throw new CommonBaseException(ResultCodeEnum.D101 , "删除房源失败");
  436. }
  437. }
  438. return new R(MsgCode.SUCCESS_CODE, MsgCode.msg_SUCCESS);
  439. }
  440. /**
  441. * 更新sceneJson(写到另一个字段), 保留原始sceneJson数据
  442. *
  443. */
  444. @RequiresRoles(value = {"admin", "edit"}, logical = Logical.OR)
  445. @ApiOperation("更新sceneJson")
  446. @PostMapping("update/sceneJson")
  447. public R updateSceneJson(@Valid @RequestBody SceneJsonDto param){
  448. HouseEntity house = houseService2.findById(param.getId());
  449. if (house == null) {
  450. log.error("房源id不存在");
  451. return new R(MsgCode.e_COMMON_3001, "房源id不存在");
  452. }
  453. house.setNewSceneJson(param.getSceneJson());
  454. house.setUpdateTime(new Date());
  455. houseService2.update(house);
  456. return new R(MsgCode.SUCCESS_CODE, MsgCode.msg_SUCCESS);
  457. }
  458. @ApiOperation("获取楼层户型图")
  459. @GetMapping("image/find/floor/{houseId}/{floor}")
  460. public R findImageByFloor(@PathVariable Long houseId, @PathVariable Integer floor){
  461. List<ImageEntity> images = imageService2.findByHouseIdAndFloor(houseId, floor);
  462. return new R(MsgCode.SUCCESS_CODE, images);
  463. }
  464. /**
  465. * 只有管理员才能分配制作人
  466. * 制作人是edit角色
  467. */
  468. @RequiresRoles(value = {"admin"}, logical = Logical.OR)
  469. @ApiOperation("获取制作人")
  470. @GetMapping("getHandler")
  471. public R getHandler(){
  472. // 根据edit权限查询用户
  473. List<UserResponse> usre = roleService.findUserByRoleKey("edit");
  474. return new R(MsgCode.SUCCESS_CODE, usre);
  475. }
  476. @RequiresRoles(value = {"admin"}, logical = Logical.OR)
  477. @ApiOperation("分配制作人")
  478. @GetMapping("setHandler/{houseId}/{userId}")
  479. public R setHandler(@PathVariable Long houseId, @PathVariable Long userId){
  480. // 根据edit权限查询用户
  481. HouseEntity houseEntity = houseService2.findById(houseId);
  482. if (houseEntity == null) {
  483. log.error("房源不存在: {}", houseId);
  484. return new R(MsgCode.e_COMMON_3001, "房源不存在");
  485. }
  486. houseEntity.setHandler(userId);
  487. houseEntity.setUpdateTime(new Date());
  488. houseService2.update(houseEntity);
  489. return new R(MsgCode.SUCCESS_CODE, MsgCode.msg_SUCCESS);
  490. }
  491. @ApiOperation("查询房源图片")
  492. @GetMapping("image/detail/{id}")
  493. public R imageDetail(@PathVariable Long id){
  494. return new R(MsgCode.SUCCESS_CODE, imageService2.findById(id));
  495. }
  496. @RequiresRoles(value = {"admin", "edit"}, logical = Logical.OR)
  497. @ApiOperation("编辑图片信息")
  498. @PostMapping("image/edit")
  499. public R imageEdit(@RequestBody ImageDto param){
  500. ImageEntity image = imageService2.findById(param.getId());
  501. if (image == null){
  502. log.error("图片id不存在: {}" , param.getId());
  503. throw new BaseRuntimeException(MsgCode.e_COMMON_3002, MsgCode.msg_COMMON_3002);
  504. }
  505. BeanUtils.copyProperties(param, image);
  506. image.setUpdateTime(new Date());
  507. imageService2.update(image);
  508. return new R(MsgCode.SUCCESS_CODE, MsgCode.msg_SUCCESS);
  509. }
  510. @RequiresRoles(value = {"admin", "edit"}, logical = Logical.OR)
  511. @ApiOperation("批量编辑图片信息")
  512. @PostMapping("image/edit/all")
  513. public R imageEdit(@RequestBody Map<Long, String> param){
  514. for (Map.Entry<Long, String> a : param.entrySet()) {
  515. ImageEntity image = imageService2.findById(a.getKey());
  516. image.setUpdateTime(new Date());
  517. image.setIssue(a.getValue());
  518. imageService2.update(image);
  519. }
  520. return new R(MsgCode.SUCCESS_CODE, MsgCode.msg_SUCCESS);
  521. }
  522. /**
  523. * 陈世超的算法
  524. * 调用算法计算场景,生产全景图
  525. *
  526. * 图片是用垂直校验后的图片
  527. *
  528. * Content传到后台会生成两个文件:floorplan.json、vision.txt
  529. */
  530. @RequiresRoles(value = {"admin", "edit"}, logical = Logical.OR)
  531. @ApiOperation("生成模型")
  532. @PostMapping("rsa/pano")
  533. @Transactional(rollbackFor = Exception.class)
  534. public R rsaPano(@Valid @RequestBody PanoDto param) throws Exception {
  535. log.info("run rsaPano");
  536. if (param == null) {
  537. log.error("参数为空");
  538. return new R(50001, "error");
  539. }
  540. HouseEntity house = houseService2.findById(param.getHouseId());
  541. if (house == null) {
  542. log.error("房源不存在: {}", param.getHouseId());
  543. return new R(MsgCode.e_COMMON_3002, MsgCode.msg_COMMON_3002);
  544. }
  545. String sceneCode = house.getSceneCode();
  546. boolean flagQ = false;
  547. // data/kanfang/10001/pano
  548. String savePath = OUTPATH + sceneCode + "/" + TypeCode.SCENE_PANO;
  549. // 检查场景码对应的pano文件夹是否存在,存在则删除里面的内容(用户从新编辑场景)
  550. if (FileUtil.isDirectory(savePath)) {
  551. FileUtil.del(savePath);
  552. log.info("删除旧目录重新计算: {}", sceneCode);
  553. }
  554. log.warn("savePath: {}", savePath);
  555. String sourcePath = savePath + "/extras/";
  556. String resultSourcePath = savePath + "/results/";
  557. try {
  558. FileUtils.createDir(sourcePath);
  559. FileUtils.createDirIfExistThenDel(resultSourcePath);
  560. /**
  561. * 以下字段为给算法部传递的参数(data.json),用于生成模型和
  562. * 照片,提供前端使用,完整的参数如下:
  563. * {
  564. * "split_type": "SPLIT_V5",
  565. * "skybox_type": "SKYBOX_V5",
  566. * "extras": {
  567. * "has_vision_txt": true,
  568. * "has_floorplan_json": true,
  569. * "has_source_images": true
  570. * }
  571. * }
  572. * */
  573. String content = "";
  574. //生成data.json供算法部使用
  575. JSONObject dataJson = new JSONObject();
  576. dataJson.put("split_type", "SPLIT_V5");
  577. String imageResolution = checkImageResolutionRate(house);
  578. if(ImageResolutionRate.TWO_K.name().equals(imageResolution)){
  579. //2 K 照片
  580. dataJson.put("skybox_type", "SKYBOX_V7");
  581. }else{
  582. //4 k照片
  583. dataJson.put("skybox_type", "SKYBOX_V6");
  584. }
  585. dataJson.put("extras", null);
  586. JSONObject extrasJson = new JSONObject();
  587. extrasJson.put("has_vision_txt" , true);
  588. extrasJson.put("has_floorplan_json" , true);
  589. extrasJson.put("has_source_images" , true);
  590. dataJson.put("extras" , extrasJson);
  591. FileUtils.fileWriter(dataJson.toJSONString(), savePath + "/data.json");
  592. log.warn("dataJson的生成路径: {}", savePath + "/data.json");
  593. // 解析json
  594. JSONObject original = JSON.parseObject(param.getContent());
  595. // 获取modelData, cameraData
  596. String floorPlan = original.getString("floorplan");
  597. String vision = original.getString("vision");
  598. if (floorPlan == null) {
  599. log.error("floorPlan不能为空");
  600. return new R(50002, "floorPlan不能为空");
  601. }
  602. if (vision == null) {
  603. log.error("vision不能为空");
  604. return new R(50002, "vision不能为空");
  605. }
  606. // json写入服务器
  607. FileUtils.fileWriter(floorPlan, sourcePath + "floorplan.json");
  608. FileUtils.fileWriter(vision, sourcePath + "vision.txt");
  609. FileUtils.fileWriter(vision, resultSourcePath + "vision.txt");
  610. log.info("floorplan.json生成完成");
  611. log.info("/extras/vision.txt,/results/vision.txt生成完成");
  612. // 复制垂直校验图片到指定目录
  613. String verticalImagePath = OUTPATH + sceneCode+"/output_img";
  614. String panoImagePath = sourcePath + "images/";
  615. // srcPath: /data/kanfang/10002/output_img
  616. log.info("srcPath: {}", verticalImagePath);
  617. // target: /data/kanfang/10002/pano/extras/images/
  618. log.info("target: {}", panoImagePath);
  619. FileUtil.copyFilesFromDir(new File(verticalImagePath), new File(panoImagePath), true);
  620. flagQ = true;
  621. } catch (Exception e) {
  622. e.printStackTrace();
  623. log.error("出错了……");
  624. return new R(50002, e.getMessage());
  625. }
  626. if (flagQ) {
  627. /* mqMap.put("id", house.getId());
  628. mqMap.put("basePath", savePath);*/
  629. // 上传floorplan.json到oss, 并命名为floor.json
  630. HashMap<String, String> uploadJson = new HashMap<>();
  631. // osspath : images/images+sceneCode/floor.json
  632. uploadJson.put(sourcePath + "floorplan.json", ConstantFilePath.OSS_FLOOR_PATH+sceneCode+"/floor.json");
  633. AliyunOssUtil.uploadMulFiles(uploadJson);
  634. // savePath:/data/kanfang/房源编号/
  635. // basePath: /data/kanfang/10002/pano
  636. log.info("basePath: {}", savePath);
  637. log.info("houseId: {}", house.getId());
  638. // 每点击生成模型一次,就修改SceneProEditEntity的部分值
  639. SceneProEntity sceneProEntity = sceneProService.findBySceneNum(house.getSceneCode());
  640. if (sceneProEntity == null) {
  641. log.error("sceneProEntity对象不存在:{}", house.getSceneCode());
  642. return new R(MsgCode.ERROR_CODE, "sceneProEntity对象不存在");
  643. }
  644. // 修改房源状态, 3:模型计算中
  645. house.setStatus(3);
  646. house.setUpdateTime(new Date());
  647. int updateHouse = houseService2.update(house);
  648. if(updateHouse != 1){
  649. throw new CommonBaseException(ResultCodeEnum.D101 , "更新房源状态失败");
  650. }
  651. SceneProEditEntity proEditEntity = sceneProEditService.findByProId(sceneProEntity.getId());
  652. proEditEntity.setVersion(proEditEntity.getVersion() + 1);
  653. proEditEntity.setFloorEditVer(proEditEntity.getFloorEditVer() + 1);
  654. proEditEntity.setFloorPublishVer(proEditEntity.getFloorPublishVer() + 1);
  655. proEditEntity.setUpdateTime(new Date());
  656. int updateSceneProEdit = sceneProEditService.update(proEditEntity);
  657. if(updateSceneProEdit != 1){
  658. throw new CommonBaseException(ResultCodeEnum.D101 , "更新sceneProEdit状态失败");
  659. }
  660. log.info("更新SceneProEditEntity数据完成");
  661. //发消息到mq
  662. Map<String, Object> mqMap = formatMqReq(house , sceneProEntity , savePath , RabbitConfig.PANO_CALLBACK_QUEUE);
  663. String mqMapStr = JSON.toJSONString(mqMap);
  664. log.info("发送mq消息给四维看看建模:{}", mqMapStr);
  665. rabbitTemplate.convertAndSend(RabbitConfig.PANO_EXCHANGE, RabbitConfig.PANO_QUEUE_ROUTING, mqMapStr);
  666. log.info("Mq入队成功");
  667. }
  668. return new R(MsgCode.SUCCESS_CODE, house);
  669. }
  670. private Map<String, Object> formatMqReq(HouseEntity house , SceneProEntity sceneProEntity , String path, String callbackQueue){
  671. Map<String ,Object> result = new HashMap<>();
  672. if(null == house){
  673. return result;
  674. }
  675. result.put("sceneName" , house.getDistrictName());
  676. result.put("sceneNum" , house.getSceneCode());
  677. result.put("sceneScheme" , sceneProEntity.getSceneScheme());
  678. result.put("userId" , house.getUserId());
  679. result.put("dataSource" , path);
  680. result.put("callbackQueue" , callbackQueue);
  681. return result;
  682. }
  683. private String checkImageResolutionRate(HouseEntity houseEntity){
  684. if(null == houseEntity || null == houseEntity.getId()){
  685. return null;
  686. }
  687. List<ImageEntity> imageEntities = imageService2.findByHouseId(houseEntity.getId());
  688. if(CollectionUtils.isEmpty(imageEntities)){
  689. log.error("房源[{}]下面无照片,默认返回2k的分辨率");
  690. return ImageResolutionRate.TWO_K.name();
  691. }
  692. int sort = 0;
  693. ImageResolutionRate maxResolution = null;
  694. for (ImageEntity imageEntity : imageEntities){
  695. if(StringUtils.isBlank(imageEntity.getResolutionRate())){
  696. log.warn("房源[{}]的照片[{}]没有设置照片分辨率,跳过" , houseEntity.getId() , imageEntity.getId());
  697. break;
  698. }
  699. ImageResolutionRate curResolution = ImageResolutionRate.getResolutionByName(imageEntity.getResolutionRate());
  700. if(null != curResolution){
  701. if(curResolution.getOrder() > sort){
  702. sort = curResolution.getOrder();
  703. maxResolution = curResolution;
  704. }
  705. }
  706. }
  707. log.info("房源[{}]的照片最大的分辨率为:{}" , houseEntity.getId() , null != maxResolution ? maxResolution.name() :"空值");
  708. return null != maxResolution ? maxResolution.name() : ImageResolutionRate.TWO_K.name();
  709. }
  710. /***
  711. * 从四维看看获取唯一场景码
  712. */
  713. private String getSceneCode(){
  714. String url = KankanHost + "api/scene/finSkSceneNum";
  715. String kankanResult = HttpClientUtil.doPost(url);
  716. //解析返回结果
  717. JSONObject res = JSONObject.parseObject(kankanResult);
  718. log.info("四维看看返回登录数据:{}" , null != res ? res.toJSONString() : "返回结果为null");
  719. Integer code = DataUtils.getInteger(res.get("code"));
  720. if(null == code || code.compareTo(0) != 0){
  721. throw new CommonBaseException(ResultCodeEnum.D101 , "从四维看看获取场景码失败");
  722. }
  723. String sceneCode = res.getString("msg");
  724. if(StringUtils.isBlank(sceneCode)){
  725. throw new CommonBaseException(ResultCodeEnum.D101 , "四维看看返回的场景码为空");
  726. }
  727. return sceneCode;
  728. }
  729. }