新增房产AI

This commit is contained in:
2026-07-30 15:43:44 +08:00
parent e6736d41ff
commit 70354148b4
36 changed files with 2654 additions and 99 deletions

View File

@@ -0,0 +1,46 @@
package com.gxwebsoft.house.controller;
import com.gxwebsoft.common.core.utils.JSONUtil;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.websocket.WebSocketServer;
import com.gxwebsoft.house.entity.HouseAiChatRequest;
import com.gxwebsoft.house.entity.HouseAiChatResponse;
import com.gxwebsoft.house.service.HouseAiChatService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* AI找房聊天控制器
*/
@Tag(name = "AI找房问答")
@RestController
@RequestMapping("/api/house/ai-chat")
public class HouseAiChatController extends BaseController {
@Resource
private HouseAiChatService houseAiChatService;
@Resource
private WebSocketServer webSocketServer;
@Operation(summary = "发送AI找房问题")
@PostMapping("/message")
public ApiResult<?> message(@RequestBody HouseAiChatRequest request) {
if (request.getUserId() == null || request.getQuestion() == null || request.getQuestion().trim().isEmpty()) {
return fail("提问内容不能为空");
}
try {
HouseAiChatResponse response = houseAiChatService.answer(request);
webSocketServer.sendMessage(String.valueOf(request.getUserId()), JSONUtil.toJSONString(response));
return success("处理成功");
} catch (Exception e) {
return fail("AI服务暂时不可用请稍后再试。");
}
}
}

View File

@@ -0,0 +1,103 @@
package com.gxwebsoft.house.controller;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseAiConfig;
import com.gxwebsoft.house.param.HouseAiConfigParam;
import com.gxwebsoft.house.service.HouseAiConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* AI找房配置控制器
*/
@Tag(name = "AI找房配置管理")
@RestController
@RequestMapping("/api/house/ai-config")
public class HouseAiConfigController extends BaseController {
@Resource
private HouseAiConfigService houseAiConfigService;
@Operation(summary = "分页查询AI找房配置")
@GetMapping("/page")
public ApiResult<PageResult<HouseAiConfig>> page(HouseAiConfigParam param) {
return success(houseAiConfigService.pageRel(param));
}
@Operation(summary = "查询全部AI找房配置")
@GetMapping()
public ApiResult<List<HouseAiConfig>> list(HouseAiConfigParam param) {
return success(houseAiConfigService.listRel(param));
}
@Operation(summary = "查询当前租户AI找房配置")
@GetMapping("/current")
public ApiResult<HouseAiConfig> current() {
return success(houseAiConfigService.getCurrentConfig(getTenantId()));
}
@Operation(summary = "根据id查询AI找房配置")
@GetMapping("/{id}")
public ApiResult<HouseAiConfig> get(@PathVariable("id") Integer id) {
return success(houseAiConfigService.getByIdRel(id));
}
@OperationLog
@Operation(summary = "添加AI找房配置")
@PostMapping()
public ApiResult<?> save(@RequestBody HouseAiConfig houseAiConfig) {
if (houseAiConfigService.save(houseAiConfig)) {
return success("添加成功");
}
return fail("添加失败");
}
@OperationLog
@Operation(summary = "修改AI找房配置")
@PutMapping()
public ApiResult<?> update(@RequestBody HouseAiConfig houseAiConfig) {
if (houseAiConfigService.updateById(houseAiConfig)) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "删除AI找房配置")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (houseAiConfigService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@OperationLog
@Operation(summary = "批量修改AI找房配置")
@PutMapping("/batch")
public ApiResult<?> updateBatch(@RequestBody BatchParam<HouseAiConfig> batchParam) {
if (batchParam.update(houseAiConfigService, "config_id")) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "批量删除AI找房配置")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (houseAiConfigService.removeByIds(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
}

View File

@@ -0,0 +1,112 @@
package com.gxwebsoft.house.controller;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.house.entity.HouseFaq;
import com.gxwebsoft.house.param.HouseFaqParam;
import com.gxwebsoft.house.service.HouseFaqService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* AI找房常见问题控制器
*/
@Tag(name = "AI找房常见问题管理")
@RestController
@RequestMapping("/api/house/house-faq")
public class HouseFaqController extends BaseController {
@Resource
private HouseFaqService houseFaqService;
@Operation(summary = "分页查询AI找房常见问题")
@GetMapping("/page")
public ApiResult<PageResult<HouseFaq>> page(HouseFaqParam param) {
return success(houseFaqService.pageRel(param));
}
@Operation(summary = "查询全部AI找房常见问题")
@GetMapping()
public ApiResult<List<HouseFaq>> list(HouseFaqParam param) {
return success(houseFaqService.listRel(param));
}
@Operation(summary = "根据id查询AI找房常见问题")
@GetMapping("/{id}")
public ApiResult<HouseFaq> get(@PathVariable("id") Integer id) {
return success(houseFaqService.getByIdRel(id));
}
@OperationLog
@Operation(summary = "添加AI找房常见问题")
@PostMapping()
public ApiResult<?> save(@RequestBody HouseFaq houseFaq) {
User loginUser = getLoginUser();
if (loginUser != null) {
houseFaq.setUserId(loginUser.getUserId());
}
if (houseFaqService.save(houseFaq)) {
return success("添加成功");
}
return fail("添加失败");
}
@OperationLog
@Operation(summary = "修改AI找房常见问题")
@PutMapping()
public ApiResult<?> update(@RequestBody HouseFaq houseFaq) {
if (houseFaqService.updateById(houseFaq)) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "删除AI找房常见问题")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (houseFaqService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@OperationLog
@Operation(summary = "批量添加AI找房常见问题")
@PostMapping("/batch")
public ApiResult<?> saveBatch(@RequestBody List<HouseFaq> list) {
if (houseFaqService.saveBatch(list)) {
return success("添加成功");
}
return fail("添加失败");
}
@OperationLog
@Operation(summary = "批量修改AI找房常见问题")
@PutMapping("/batch")
public ApiResult<?> updateBatch(@RequestBody BatchParam<HouseFaq> batchParam) {
if (batchParam.update(houseFaqService, "faq_id")) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "批量删除AI找房常见问题")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (houseFaqService.removeByIds(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
}

View File

@@ -0,0 +1,145 @@
package com.gxwebsoft.house.controller;
import cn.hutool.core.util.StrUtil;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.house.entity.HouseMessage;
import com.gxwebsoft.house.param.HouseMessageParam;
import com.gxwebsoft.house.service.HouseMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.regex.Pattern;
/**
* AI找房留言控制器
*/
@Tag(name = "AI找房留言管理")
@RestController
@RequestMapping("/api/house/house-message")
public class HouseMessageController extends BaseController {
private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
private static final Pattern WECHAT_PATTERN = Pattern.compile("^[a-zA-Z][-_a-zA-Z0-9]{5,19}$");
@Resource
private HouseMessageService houseMessageService;
@Operation(summary = "分页查询AI找房留言")
@GetMapping("/page")
public ApiResult<PageResult<HouseMessage>> page(HouseMessageParam param) {
return success(houseMessageService.pageRel(param));
}
@Operation(summary = "查询全部AI找房留言")
@GetMapping()
public ApiResult<List<HouseMessage>> list(HouseMessageParam param) {
return success(houseMessageService.listRel(param));
}
@Operation(summary = "根据id查询AI找房留言")
@GetMapping("/{id}")
public ApiResult<HouseMessage> get(@PathVariable("id") Integer id) {
return success(houseMessageService.getByIdRel(id));
}
@OperationLog
@Operation(summary = "添加AI找房留言")
@PostMapping()
public ApiResult<?> save(@RequestBody HouseMessage houseMessage) {
String error = validateMessage(houseMessage);
if (error != null) {
return fail(error);
}
User loginUser = getLoginUser();
if (loginUser != null) {
houseMessage.setUserId(loginUser.getUserId());
}
houseMessage.setRealName(houseMessage.getRealName().trim());
houseMessage.setPhone(StrUtil.trimToNull(houseMessage.getPhone()));
houseMessage.setWechat(StrUtil.trimToNull(houseMessage.getWechat()));
houseMessage.setSource(StrUtil.blankToDefault(houseMessage.getSource(), "ai_house"));
if (houseMessage.getStatus() == null) {
houseMessage.setStatus(0);
}
if (houseMessageService.save(houseMessage)) {
return success("提交成功");
}
return fail("提交失败");
}
@OperationLog
@Operation(summary = "修改AI找房留言")
@PutMapping()
public ApiResult<?> update(@RequestBody HouseMessage houseMessage) {
String error = validateMessage(houseMessage);
if (error != null) {
return fail(error);
}
houseMessage.setRealName(houseMessage.getRealName().trim());
houseMessage.setPhone(StrUtil.trimToNull(houseMessage.getPhone()));
houseMessage.setWechat(StrUtil.trimToNull(houseMessage.getWechat()));
if (houseMessageService.updateById(houseMessage)) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "删除AI找房留言")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (houseMessageService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@OperationLog
@Operation(summary = "批量修改AI找房留言")
@PutMapping("/batch")
public ApiResult<?> updateBatch(@RequestBody BatchParam<HouseMessage> batchParam) {
if (batchParam.update(houseMessageService, "message_id")) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "批量删除AI找房留言")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (houseMessageService.removeByIds(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
private String validateMessage(HouseMessage houseMessage) {
if (houseMessage == null || StrUtil.isBlank(houseMessage.getRealName())) {
return "请输入姓名";
}
if (houseMessage.getRealName().trim().length() > 30) {
return "姓名不能超过30个字符";
}
boolean hasPhone = StrUtil.isNotBlank(houseMessage.getPhone());
boolean hasWechat = StrUtil.isNotBlank(houseMessage.getWechat());
if (!hasPhone && !hasWechat) {
return "手机号和微信号请至少填写一项";
}
if (hasPhone && !PHONE_PATTERN.matcher(houseMessage.getPhone().trim()).matches()) {
return "手机号格式不正确";
}
if (hasWechat && !WECHAT_PATTERN.matcher(houseMessage.getWechat().trim()).matches()) {
return "微信号格式不正确";
}
return null;
}
}

View File

@@ -0,0 +1,24 @@
package com.gxwebsoft.house.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* AI找房提问请求
*/
@Data
@Schema(name = "HouseAiChatRequest对象", description = "AI找房提问请求")
public class HouseAiChatRequest implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "会话ID")
private String conversationId;
@Schema(description = "用户ID")
private Integer userId;
@Schema(description = "问题")
private String question;
}

View File

@@ -0,0 +1,35 @@
package com.gxwebsoft.house.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* AI找房应答
*/
@Data
@Schema(name = "HouseAiChatResponse对象", description = "AI找房应答")
public class HouseAiChatResponse implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "消息类型")
private String type = "house_ai_result";
@Schema(description = "回答文本")
private String answer;
@Schema(description = "命中的常见问题")
private List<HouseFaq> faqs = new ArrayList<>();
@Schema(description = "推荐房源")
private List<HouseAiHouseCard> houses = new ArrayList<>();
@Schema(description = "语义解析结果")
private HouseAiIntent intent;
@Schema(description = "来源 faq/house/ai")
private String source;
}

View File

@@ -0,0 +1,53 @@
package com.gxwebsoft.house.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* AI找房配置
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "HouseAiConfig对象", description = "AI找房配置")
public class HouseAiConfig implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@TableId(value = "config_id", type = IdType.AUTO)
private Integer configId;
@Schema(description = "AI形象照")
private String aiAvatar;
@Schema(description = "首页AI入口图")
private String aiEntryImage;
@Schema(description = "首页AI悬浮图")
private String aiFloatImage;
@Schema(description = "欢迎词")
private String welcomeMessage;
@Schema(description = "状态 0正常 1禁用")
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@Schema(description = "租户ID")
private Integer tenantId;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "更新时间")
private Date updateTime;
}

View File

@@ -0,0 +1,51 @@
package com.gxwebsoft.house.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* AI找房返回的轻量房源卡片
*/
@Data
@Schema(name = "HouseAiHouseCard对象", description = "AI找房返回的轻量房源卡片")
public class HouseAiHouseCard implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "房源ID")
private Integer houseId;
@Schema(description = "房源标题")
private String houseTitle;
@Schema(description = "户型")
private String houseType;
@Schema(description = "面积")
private String extent;
@Schema(description = "楼层")
private String floor;
@Schema(description = "朝向")
private String toward;
@Schema(description = "月租金")
private String monthlyRent;
@Schema(description = "所在城市")
private String city;
@Schema(description = "所在辖区")
private String region;
@Schema(description = "详细地址")
private String address;
@Schema(description = "图片附件")
private String files;
@Schema(description = "办公室配套")
private String supporting;
}

View File

@@ -0,0 +1,87 @@
package com.gxwebsoft.house.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* AI找房语义解析结果
*/
@Data
@Schema(name = "HouseAiIntent对象", description = "AI找房语义解析结果")
public class HouseAiIntent implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "原始问题")
private String originalQuestion;
@Schema(description = "意图类型 faq/house/mixed/unknown")
private String intentType;
@Schema(description = "归一化问题")
private String normalizedQuestion;
@Schema(description = "面积最小值")
private Integer extentMin;
@Schema(description = "面积最大值")
private Integer extentMax;
@Schema(description = "楼层最小值")
private Integer floorMin;
@Schema(description = "楼层最大值")
private Integer floorMax;
@Schema(description = "月租最小值")
private BigDecimal monthlyRentMin;
@Schema(description = "月租最大值")
private BigDecimal monthlyRentMax;
@Schema(description = "售价最小值")
private BigDecimal salePriceMin;
@Schema(description = "售价最大值")
private BigDecimal salePriceMax;
@Schema(description = "总价最小值")
private BigDecimal totalPriceMin;
@Schema(description = "总价最大值")
private BigDecimal totalPriceMax;
@Schema(description = "区域/地段")
private String regionKeyword;
@Schema(description = "城市")
private String cityKeyword;
@Schema(description = "租售类型 rent/sale")
private String tradeType;
@Schema(description = "装修类型")
private String decorationType;
@Schema(description = "配套要求")
private String supportingKeyword;
@Schema(description = "朝向")
private String toward;
@Schema(description = "房型")
private String houseType;
@Schema(description = "AI生成的SQL筛选条件片段")
private String whereSql;
@Schema(description = "AI生成的SQL排序片段")
private String orderSql;
@Schema(description = "其他关键词")
private List<String> tags = new ArrayList<>();
}

View File

@@ -0,0 +1,59 @@
package com.gxwebsoft.house.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* AI找房常见问题
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "HouseFaq对象", description = "AI找房常见问题")
public class HouseFaq implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@TableId(value = "faq_id", type = IdType.AUTO)
private Integer faqId;
@Schema(description = "问题")
private String question;
@Schema(description = "关键词,多个用逗号分隔")
private String keywords;
@Schema(description = "标准回答")
private String answer;
@Schema(description = "分类")
private String category;
@Schema(description = "排序号")
private Integer sortNumber;
@Schema(description = "状态 0正常 1禁用")
private Integer status;
@Schema(description = "创建用户ID")
private Integer userId;
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@Schema(description = "租户ID")
private Integer tenantId;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "更新时间")
private Date updateTime;
}

View File

@@ -0,0 +1,62 @@
package com.gxwebsoft.house.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* AI找房留言
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "HouseMessage对象", description = "AI找房留言")
public class HouseMessage implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@TableId(value = "message_id", type = IdType.AUTO)
private Integer messageId;
@Schema(description = "用户ID")
private Integer userId;
@Schema(description = "姓名")
private String realName;
@Schema(description = "手机号")
private String phone;
@Schema(description = "微信号")
private String wechat;
@Schema(description = "来源")
private String source;
@Schema(description = "备注")
private String comments;
@Schema(description = "状态 0未处理 1已处理")
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@Schema(description = "租户ID")
private Integer tenantId;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,19 @@
package com.gxwebsoft.house.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.house.entity.HouseAiConfig;
import com.gxwebsoft.house.param.HouseAiConfigParam;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* AI找房配置Mapper
*/
public interface HouseAiConfigMapper extends BaseMapper<HouseAiConfig> {
List<HouseAiConfig> selectPageRel(@Param("page") IPage<HouseAiConfig> page, @Param("param") HouseAiConfigParam param);
List<HouseAiConfig> selectListRel(@Param("param") HouseAiConfigParam param);
}

View File

@@ -0,0 +1,19 @@
package com.gxwebsoft.house.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.house.entity.HouseFaq;
import com.gxwebsoft.house.param.HouseFaqParam;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* AI找房常见问题Mapper
*/
public interface HouseFaqMapper extends BaseMapper<HouseFaq> {
List<HouseFaq> selectPageRel(@Param("page") IPage<HouseFaq> page, @Param("param") HouseFaqParam param);
List<HouseFaq> selectListRel(@Param("param") HouseFaqParam param);
}

View File

@@ -34,4 +34,9 @@ public interface HouseInfoMapper extends BaseMapper<HouseInfo> {
*/ */
List<HouseInfo> selectListRel(@Param("param") HouseInfoParam param); List<HouseInfo> selectListRel(@Param("param") HouseInfoParam param);
/**
* 执行AI生成的受控查询条件
*/
List<HouseInfo> selectListByAiSql(@Param("whereSql") String whereSql, @Param("orderSql") String orderSql);
} }

View File

@@ -0,0 +1,19 @@
package com.gxwebsoft.house.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.house.entity.HouseMessage;
import com.gxwebsoft.house.param.HouseMessageParam;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* AI找房留言Mapper
*/
public interface HouseMessageMapper extends BaseMapper<HouseMessage> {
List<HouseMessage> selectPageRel(@Param("page") IPage<HouseMessage> page, @Param("param") HouseMessageParam param);
List<HouseMessage> selectListRel(@Param("param") HouseMessageParam param);
}

View File

@@ -0,0 +1,56 @@
<?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.gxwebsoft.house.mapper.HouseAiConfigMapper">
<sql id="selectSql">
SELECT a.*
FROM house_ai_config a
<where>
<if test="param.configId != null">
AND a.config_id = #{param.configId}
</if>
<if test="param.aiAvatar != null and param.aiAvatar != ''">
AND a.ai_avatar LIKE CONCAT('%', #{param.aiAvatar}, '%')
</if>
<if test="param.aiEntryImage != null and param.aiEntryImage != ''">
AND a.ai_entry_image LIKE CONCAT('%', #{param.aiEntryImage}, '%')
</if>
<if test="param.aiFloatImage != null and param.aiFloatImage != ''">
AND a.ai_float_image LIKE CONCAT('%', #{param.aiFloatImage}, '%')
</if>
<if test="param.welcomeMessage != null and param.welcomeMessage != ''">
AND a.welcome_message LIKE CONCAT('%', #{param.welcomeMessage}, '%')
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.tenantId != null">
AND a.tenant_id = #{param.tenantId}
</if>
<if test="param.keywords != null and param.keywords != ''">
AND (
a.welcome_message LIKE CONCAT('%', #{param.keywords}, '%')
OR a.ai_avatar LIKE CONCAT('%', #{param.keywords}, '%')
OR a.ai_entry_image LIKE CONCAT('%', #{param.keywords}, '%')
OR a.ai_float_image LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>
</where>
ORDER BY a.config_id DESC
</sql>
<select id="selectPageRel" resultType="com.gxwebsoft.house.entity.HouseAiConfig">
<include refid="selectSql"></include>
</select>
<select id="selectListRel" resultType="com.gxwebsoft.house.entity.HouseAiConfig">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -0,0 +1,65 @@
<?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.gxwebsoft.house.mapper.HouseFaqMapper">
<sql id="selectSql">
SELECT a.*
FROM house_faq a
<where>
<if test="param.faqId != null">
AND a.faq_id = #{param.faqId}
</if>
<if test="param.question != null and param.question != ''">
AND a.question LIKE CONCAT('%', #{param.question}, '%')
</if>
<if test="param.keywordsText != null and param.keywordsText != ''">
AND a.keywords LIKE CONCAT('%', #{param.keywordsText}, '%')
</if>
<if test="param.answer != null and param.answer != ''">
AND a.answer LIKE CONCAT('%', #{param.answer}, '%')
</if>
<if test="param.category != null and param.category != ''">
AND a.category LIKE CONCAT('%', #{param.category}, '%')
</if>
<if test="param.sortNumber != null">
AND a.sort_number = #{param.sortNumber}
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.userId != null">
AND a.user_id = #{param.userId}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.createTimeStart != null">
AND a.create_time &gt;= #{param.createTimeStart}
</if>
<if test="param.createTimeEnd != null">
AND a.create_time &lt;= #{param.createTimeEnd}
</if>
<if test="param.keywords != null and param.keywords != ''">
AND (
a.question LIKE CONCAT('%', #{param.keywords}, '%')
OR a.keywords LIKE CONCAT('%', #{param.keywords}, '%')
OR a.answer LIKE CONCAT('%', #{param.keywords}, '%')
OR a.category LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>
</where>
ORDER BY a.sort_number ASC, a.faq_id DESC
</sql>
<select id="selectPageRel" resultType="com.gxwebsoft.house.entity.HouseFaq">
<include refid="selectSql"></include>
</select>
<select id="selectListRel" resultType="com.gxwebsoft.house.entity.HouseFaq">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -172,4 +172,25 @@
<include refid="selectSql"></include> <include refid="selectSql"></include>
</select> </select>
<!-- AI受控查询 -->
<select id="selectListByAiSql" resultType="com.gxwebsoft.house.entity.HouseInfo">
SELECT a.*,
b.nickname,b.avatar,b.grade_id, b.phone as userPhone
FROM house_info a
LEFT JOIN gxwebsoft_core.sys_user b ON a.user_id = b.user_id
<where>
a.deleted = 0
AND a.status = 0
<if test="whereSql != null and whereSql != ''">
AND ${whereSql}
</if>
</where>
<if test="orderSql != null and orderSql != ''">
ORDER BY ${orderSql}
</if>
<if test="orderSql == null or orderSql == ''">
ORDER BY a.sort_number asc, a.create_time desc
</if>
</select>
</mapper> </mapper>

View File

@@ -0,0 +1,64 @@
<?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.gxwebsoft.house.mapper.HouseMessageMapper">
<sql id="selectSql">
SELECT a.*
FROM house_message a
<where>
<if test="param.messageId != null">
AND a.message_id = #{param.messageId}
</if>
<if test="param.userId != null">
AND a.user_id = #{param.userId}
</if>
<if test="param.realName != null and param.realName != ''">
AND a.real_name LIKE CONCAT('%', #{param.realName}, '%')
</if>
<if test="param.phone != null and param.phone != ''">
AND a.phone LIKE CONCAT('%', #{param.phone}, '%')
</if>
<if test="param.wechat != null and param.wechat != ''">
AND a.wechat LIKE CONCAT('%', #{param.wechat}, '%')
</if>
<if test="param.source != null and param.source != ''">
AND a.source = #{param.source}
</if>
<if test="param.comments != null and param.comments != ''">
AND a.comments LIKE CONCAT('%', #{param.comments}, '%')
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.createTimeStart != null">
AND a.create_time &gt;= #{param.createTimeStart}
</if>
<if test="param.createTimeEnd != null">
AND a.create_time &lt;= #{param.createTimeEnd}
</if>
<if test="param.keywords != null and param.keywords != ''">
AND (
a.real_name LIKE CONCAT('%', #{param.keywords}, '%')
OR a.phone LIKE CONCAT('%', #{param.keywords}, '%')
OR a.wechat LIKE CONCAT('%', #{param.keywords}, '%')
OR a.comments LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>
</where>
</sql>
<select id="selectPageRel" resultType="com.gxwebsoft.house.entity.HouseMessage">
<include refid="selectSql"></include>
</select>
<select id="selectListRel" resultType="com.gxwebsoft.house.entity.HouseMessage">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -0,0 +1,44 @@
package com.gxwebsoft.house.param;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AI找房配置查询参数
*/
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(name = "HouseAiConfigParam对象", description = "AI找房配置查询参数")
public class HouseAiConfigParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@QueryField(type = QueryType.EQ)
private Integer configId;
@Schema(description = "AI形象照")
private String aiAvatar;
@Schema(description = "首页AI入口图")
private String aiEntryImage;
@Schema(description = "首页AI悬浮图")
private String aiFloatImage;
@Schema(description = "欢迎词")
private String welcomeMessage;
@Schema(description = "状态 0正常 1禁用")
@QueryField(type = QueryType.EQ)
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@QueryField(type = QueryType.EQ)
private Integer deleted;
}

View File

@@ -0,0 +1,57 @@
package com.gxwebsoft.house.param;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AI找房常见问题查询参数
*/
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(name = "HouseFaqParam对象", description = "AI找房常见问题查询参数")
public class HouseFaqParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@QueryField(type = QueryType.EQ)
private Integer faqId;
@Schema(description = "问题")
private String question;
@Schema(description = "关键词")
private String keywordsText;
@Schema(description = "回答")
private String answer;
@Schema(description = "分类")
private String category;
@Schema(description = "排序号")
@QueryField(type = QueryType.EQ)
private Integer sortNumber;
@Schema(description = "状态 0正常 1禁用")
@QueryField(type = QueryType.EQ)
private Integer status;
@Schema(description = "创建用户ID")
@QueryField(type = QueryType.EQ)
private Integer userId;
@Schema(description = "是否删除, 0否, 1是")
@QueryField(type = QueryType.EQ)
private Integer deleted;
@Schema(description = "语义搜索原句")
@TableField(exist = false)
private String queryText;
}

View File

@@ -0,0 +1,54 @@
package com.gxwebsoft.house.param;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AI找房留言查询参数
*/
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(name = "HouseMessageParam对象", description = "AI找房留言查询参数")
public class HouseMessageParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "主键ID")
@QueryField(type = QueryType.EQ)
private Integer messageId;
@Schema(description = "用户ID")
@QueryField(type = QueryType.EQ)
private Integer userId;
@Schema(description = "姓名")
private String realName;
@Schema(description = "手机号")
private String phone;
@Schema(description = "微信号")
private String wechat;
@Schema(description = "来源")
private String source;
@Schema(description = "备注")
private String comments;
@Schema(description = "状态 0未处理 1已处理")
@QueryField(type = QueryType.EQ)
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@QueryField(type = QueryType.EQ)
private Integer deleted;
@Schema(description = "关键词")
private String keywords;
}

View File

@@ -0,0 +1,15 @@
package com.gxwebsoft.house.service;
import com.gxwebsoft.house.entity.HouseAiChatRequest;
import com.gxwebsoft.house.entity.HouseAiChatResponse;
import com.gxwebsoft.house.entity.HouseAiIntent;
/**
* AI找房问答Service
*/
public interface HouseAiChatService {
HouseAiIntent analyzeIntent(String question);
HouseAiChatResponse answer(HouseAiChatRequest request);
}

View File

@@ -0,0 +1,22 @@
package com.gxwebsoft.house.service;
import com.github.yulichang.base.MPJBaseService;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseAiConfig;
import com.gxwebsoft.house.param.HouseAiConfigParam;
import java.util.List;
/**
* AI找房配置Service
*/
public interface HouseAiConfigService extends MPJBaseService<HouseAiConfig> {
PageResult<HouseAiConfig> pageRel(HouseAiConfigParam param);
List<HouseAiConfig> listRel(HouseAiConfigParam param);
HouseAiConfig getByIdRel(Integer configId);
HouseAiConfig getCurrentConfig(Integer tenantId);
}

View File

@@ -0,0 +1,22 @@
package com.gxwebsoft.house.service;
import com.github.yulichang.base.MPJBaseService;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseFaq;
import com.gxwebsoft.house.param.HouseFaqParam;
import java.util.List;
/**
* AI找房常见问题Service
*/
public interface HouseFaqService extends MPJBaseService<HouseFaq> {
PageResult<HouseFaq> pageRel(HouseFaqParam param);
List<HouseFaq> listRel(HouseFaqParam param);
HouseFaq getByIdRel(Integer faqId);
List<HouseFaq> findBestMatches(String queryText, int limit);
}

View File

@@ -0,0 +1,20 @@
package com.gxwebsoft.house.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseMessage;
import com.gxwebsoft.house.param.HouseMessageParam;
import java.util.List;
/**
* AI找房留言Service
*/
public interface HouseMessageService extends IService<HouseMessage> {
PageResult<HouseMessage> pageRel(HouseMessageParam param);
List<HouseMessage> listRel(HouseMessageParam param);
HouseMessage getByIdRel(Integer messageId);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
package com.gxwebsoft.house.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseAiConfig;
import com.gxwebsoft.house.mapper.HouseAiConfigMapper;
import com.gxwebsoft.house.param.HouseAiConfigParam;
import com.gxwebsoft.house.service.HouseAiConfigService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* AI找房配置Service实现
*/
@Service
public class HouseAiConfigServiceImpl extends ServiceImpl<HouseAiConfigMapper, HouseAiConfig> implements HouseAiConfigService {
@Override
public PageResult<HouseAiConfig> pageRel(HouseAiConfigParam param) {
PageParam<HouseAiConfig, HouseAiConfigParam> page = new PageParam<>(param);
page.setDefaultOrder("config_id desc");
List<HouseAiConfig> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal());
}
@Override
public List<HouseAiConfig> listRel(HouseAiConfigParam param) {
return baseMapper.selectListRel(param);
}
@Override
public HouseAiConfig getByIdRel(Integer configId) {
HouseAiConfigParam param = new HouseAiConfigParam();
param.setConfigId(configId);
return param.getOne(baseMapper.selectListRel(param));
}
@Override
public HouseAiConfig getCurrentConfig(Integer tenantId) {
HouseAiConfigParam param = new HouseAiConfigParam();
param.setStatus(0);
param.setTenantId(tenantId);
HouseAiConfig config = param.getOne(baseMapper.selectListRel(param));
if (config != null) {
return config;
}
param.setTenantId(null);
return param.getOne(baseMapper.selectListRel(param));
}
}

View File

@@ -0,0 +1,151 @@
package com.gxwebsoft.house.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseFaq;
import com.gxwebsoft.house.mapper.HouseFaqMapper;
import com.gxwebsoft.house.param.HouseFaqParam;
import com.gxwebsoft.house.service.HouseFaqService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
/**
* AI找房常见问题Service实现
*/
@Service
public class HouseFaqServiceImpl extends ServiceImpl<HouseFaqMapper, HouseFaq> implements HouseFaqService {
@Override
public PageResult<HouseFaq> pageRel(HouseFaqParam param) {
PageParam<HouseFaq, HouseFaqParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, faq_id desc");
List<HouseFaq> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal());
}
@Override
public List<HouseFaq> listRel(HouseFaqParam param) {
return baseMapper.selectListRel(param);
}
@Override
public HouseFaq getByIdRel(Integer faqId) {
HouseFaqParam param = new HouseFaqParam();
param.setFaqId(faqId);
return param.getOne(baseMapper.selectListRel(param));
}
@Override
public List<HouseFaq> findBestMatches(String queryText, int limit) {
HouseFaqParam param = new HouseFaqParam();
param.setStatus(0);
List<HouseFaq> all = baseMapper.selectListRel(param);
if (StrUtil.isBlank(queryText) || all == null || all.isEmpty()) {
return new ArrayList<>();
}
final String normalizedQuery = normalizeText(queryText);
return all.stream()
.map(item -> new ScoredFaq(item, score(item, normalizedQuery)))
.filter(item -> item.score > 0)
.sorted(Comparator.comparingInt(ScoredFaq::getScore).reversed()
.thenComparing(item -> item.faq.getSortNumber() == null ? Integer.MAX_VALUE : item.faq.getSortNumber()))
.limit(limit)
.map(ScoredFaq::getFaq)
.collect(Collectors.toList());
}
private int score(HouseFaq faq, String normalizedQuery) {
int score = 0;
String question = normalizeText(faq.getQuestion());
String keywords = normalizeText(faq.getKeywords());
String answer = normalizeText(faq.getAnswer());
String category = normalizeText(faq.getCategory());
if (StrUtil.isBlank(normalizedQuery)) {
return score;
}
if (question.contains(normalizedQuery)) {
score += 120;
}
if (keywords.contains(normalizedQuery)) {
score += 100;
}
if (answer.contains(normalizedQuery)) {
score += 40;
}
if (category.contains(normalizedQuery)) {
score += 30;
}
for (String token : tokenize(normalizedQuery)) {
if (token.length() < 2) {
continue;
}
if (question.contains(token)) {
score += 20;
}
if (keywords.contains(token)) {
score += 18;
}
if (answer.contains(token)) {
score += 8;
}
if (category.contains(token)) {
score += 6;
}
}
return score;
}
private List<String> tokenize(String text) {
String normalized = normalizeText(text);
List<String> tokens = new ArrayList<>();
for (String item : normalized.split("[,\\s+/|]+")) {
if (StrUtil.isNotBlank(item)) {
tokens.add(item);
}
}
return tokens;
}
private String normalizeText(String text) {
if (text == null) {
return "";
}
return text.replace(" ", " ")
.replace("", ",")
.replace("", " ")
.replace("", " ")
.replace("", " ")
.replace("", "(")
.replace("", ")")
.replace("", "")
.replace("", "")
.replace("", "")
.toLowerCase(Locale.ROOT)
.trim();
}
private static class ScoredFaq {
private final HouseFaq faq;
private final int score;
private ScoredFaq(HouseFaq faq, int score) {
this.faq = faq;
this.score = score;
}
public HouseFaq getFaq() {
return faq;
}
public int getScore() {
return score;
}
}
}

View File

@@ -0,0 +1,42 @@
package com.gxwebsoft.house.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.house.entity.HouseMessage;
import com.gxwebsoft.house.mapper.HouseMessageMapper;
import com.gxwebsoft.house.param.HouseMessageParam;
import com.gxwebsoft.house.service.HouseMessageService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* AI找房留言Service实现
*/
@Service
public class HouseMessageServiceImpl extends ServiceImpl<HouseMessageMapper, HouseMessage> implements HouseMessageService {
@Override
public PageResult<HouseMessage> pageRel(HouseMessageParam param) {
PageParam<HouseMessage, HouseMessageParam> page = new PageParam<>(param);
page.setDefaultOrder("create_time desc, message_id desc");
List<HouseMessage> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal());
}
@Override
public List<HouseMessage> listRel(HouseMessageParam param) {
List<HouseMessage> list = baseMapper.selectListRel(param);
PageParam<HouseMessage, HouseMessageParam> page = new PageParam<>();
page.setDefaultOrder("create_time desc, message_id desc");
return page.sortRecords(list);
}
@Override
public HouseMessage getByIdRel(Integer messageId) {
HouseMessageParam param = new HouseMessageParam();
param.setMessageId(messageId);
return param.getOne(baseMapper.selectListRel(param));
}
}

View File

@@ -32,7 +32,7 @@ socketio:
# MQTT配置 # MQTT配置
mqtt: mqtt:
enabled: false # 添加开关来禁用MQTT服务 enabled: false # 添加开关来禁用MQTT服务
host: tcp://132.232.214.96:1883 host: tcp://1.14.159.185:1883
username: swdev username: swdev
password: Sw20250523 password: Sw20250523
client-id-prefix: hjm_car_ client-id-prefix: hjm_car_
@@ -46,7 +46,7 @@ mqtt:
config: config:
# 开发环境接口 # 开发环境接口
server-url: https://server.websoft.top/api server-url: https://server.websoft.top/api
upload-path: /Users/gxwebsoft/Documents/uploads/ # window(D:\Temp) upload-path: /Users/gxwebsoft/JAVA/mp-java/src/main/resources/ # window(D:\Temp)
# 开发环境证书配置 # 开发环境证书配置
certificate: certificate:
@@ -56,10 +56,3 @@ certificate:
private-key-file: "apiclient_key.pem" private-key-file: "apiclient_key.pem"
apiclient-cert-file: "apiclient_cert.pem" apiclient-cert-file: "apiclient_cert.pem"
wechatpay-cert-file: "wechatpay_cert.pem" wechatpay-cert-file: "wechatpay_cert.pem"
# 阿里云翻译配置
aliyun:
translate:
access-key-id: LTAI5tEsyhW4GCKbds1qsopg
access-key-secret: zltFlQrYVAoq2KMFDWgLa3GhkMNeyO
endpoint: mt.cn-hangzhou.aliyuncs.com

View File

@@ -22,13 +22,6 @@ spring:
jackson: jackson:
time-zone: GMT+8 time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss
serialization:
write-dates-as-timestamps: false
deserialization:
fail-on-unknown-properties: false
# 确保启用Java 8时间支持
modules:
- com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
# 连接池配置 # 连接池配置
datasource: datasource:
@@ -99,11 +92,10 @@ config:
# 主服务器 # 主服务器
server-url: https://server.websoft.top/api server-url: https://server.websoft.top/api
# 文件服务器 # 文件服务器
file-server: https://file.websoft.top file-server: https://file.wsdns.cn
# 其他 upload-path: /Users/gxwebsoft/Documents/uploads/
api-url: https://server.websoft.top/api local-upload-path: /Users/gxwebsoft/Documents/uploads/
upload-path: /Users/gxwebsoft/Documents/uploads api-url: https://cms-api.websoft.top/api
local-upload-path: /Users/gxwebsoft/Documents/uploads
# 阿里云OSS云存储 # 阿里云OSS云存储
endpoint: https://oss-cn-shenzhen.aliyuncs.com endpoint: https://oss-cn-shenzhen.aliyuncs.com
@@ -118,18 +110,18 @@ shop:
order: order:
# 测试账号配置 # 测试账号配置
test-account: test-account:
enabled: true # 禁用测试账号功能 enabled: true
phone-numbers: phone-numbers:
- "19163679581" # 改为其他测试手机号 - "13737128880"
test-pay-amount: 0.01 test-pay-amount: 0.01
# 租户特殊规则配置 # 租户特殊规则配置
# tenant-rules: tenant-rules:
# - tenant-id: 10324 - tenant-id: 10324
# tenant-name: "百色中学" tenant-name: "百色中学"
# min-amount: 10 min-amount: 10
# min-amount-message: "捐款金额最低不能少于10元感谢您的爱心捐赠^_^" min-amount-message: "捐款金额最低不能少于10元感谢您的爱心捐赠^_^"
# enabled: true enabled: true
# 默认配置 # 默认配置
default-config: default-config:
@@ -138,32 +130,6 @@ shop:
min-order-amount: 0 min-order-amount: 0
order-timeout-minutes: 30 order-timeout-minutes: 30
# 订单自动取消配置
auto-cancel:
# 是否启用自动取消功能
enabled: true
# 默认超时时间(分钟)
default-timeout-minutes: 30
# 定时任务检查间隔(分钟)
check-interval-minutes: 1
# 批量处理大小
batch-size: 100
# 定时任务执行时间cron表达式
# 生产环境每5分钟执行一次
cron: "0 */5 * * * ?"
# 租户特殊配置
# tenant-configs:
# - tenant-id: 10324
# tenant-name: "百色中学"
# timeout-minutes: 120 # 捐款订单给更长的支付时间
# enabled: true
# 可以添加更多租户配置
# - tenant-id: 10550
# tenant-name: "其他租户"
# timeout-minutes: 15
# enabled: true
# 证书配置 # 证书配置
certificate: certificate:
# 证书加载模式: CLASSPATH, FILESYSTEM, VOLUME # 证书加载模式: CLASSPATH, FILESYSTEM, VOLUME
@@ -198,50 +164,6 @@ springdoc:
swagger-ui: swagger-ui:
enabled: true enabled: true
# LED - 排班接口(业务中台)对接配置
led:
bme:
base-url: ${LED_BME_BASE_URL:http://16.1.4.201:7979}
appid: ${LED_BME_APPID:BQ73n58Lf}
secret-key: ${LED_BME_SECRET_KEY:jk720-DCPnGq@5t8}
mechanism-id: ${LED_BME_MECHANISM_ID:10001}
default-ext-user-id: ${LED_BME_DEFAULT_EXT_USER_ID:txzhyy}
default-hospital-id: ${LED_BME_DEFAULT_HOSPITAL_ID:}
timeout-ms: ${LED_BME_TIMEOUT_MS:10000}
# 启用 Knife4j # 启用 Knife4j
knife4j: knife4j:
enable: true enable: true
# 优惠券配置
coupon:
# 过期处理定时任务配置
expire:
# 定时任务执行时间cron表达式
# 生产环境每天凌晨2点执行
# 开发环境每10分钟执行一次
cron: "0 0 2 * * ?"
# 开发环境可以设置为: "0 */10 * * * ?"
# 状态管理配置
status:
# 是否启用自动状态更新
auto-update: true
# 批量处理大小
batch-size: 1000
# 支付配置
payment:
# 开发环境配置
dev:
# 开发环境回调地址(本地调试用)
notify-url: "http://frps-10550.s209.websoft.top/api/shop/shop-order/notify"
# 开发环境是否启用环境感知
environment-aware: true
# 生产环境配置
prod:
# 生产环境回调地址
notify-url: "https://cms-api.websoft.top/api/shop/shop-order/notify"
# 生产环境是否启用环境感知
environment-aware: false

View File

@@ -0,0 +1,24 @@
CREATE TABLE `house_ai_config` (
`config_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`ai_avatar` varchar(500) DEFAULT NULL COMMENT 'AI形象照',
`ai_entry_image` varchar(500) DEFAULT NULL COMMENT '首页AI入口图',
`ai_float_image` varchar(500) DEFAULT NULL COMMENT '首页AI悬浮图',
`welcome_message` varchar(500) DEFAULT NULL COMMENT '欢迎词',
`status` tinyint(1) DEFAULT 0 COMMENT '状态 0正常 1禁用',
`deleted` tinyint(1) DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`config_id`),
KEY `idx_house_ai_config_status` (`status`),
KEY `idx_house_ai_config_tenant` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI找房配置';
INSERT INTO `house_ai_config` (`ai_avatar`, `ai_entry_image`, `ai_float_image`, `welcome_message`, `status`)
VALUES
('https://oss.wsdns.cn/20260601/0d53634419a448919c90c99ab9779d8a.png?x-oss-process=image/resize,w_750/quality,Q_90', 'https://oss.wsdns.cn/20260626/f5f7d5996e4f45ce8bb24c7c455d07d4.png?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90', 'https://oss.wsdns.cn/20260626/e666a52de6724578b6df007fcacbbe21.gif?x-oss-process=image/resize,m_fixed,w_750/quality,Q_90', '您好我是AI找房助手请告诉我面积、楼层、预算和地段我来帮您筛选更合适的房源。', 0);
-- 已有表升级时执行:
-- ALTER TABLE `house_ai_config`
-- ADD COLUMN `ai_entry_image` varchar(500) DEFAULT NULL COMMENT '首页AI入口图' AFTER `ai_avatar`,
-- ADD COLUMN `ai_float_image` varchar(500) DEFAULT NULL COMMENT '首页AI悬浮图' AFTER `ai_entry_image`;

View File

@@ -0,0 +1,24 @@
CREATE TABLE `house_faq` (
`faq_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`question` varchar(255) NOT NULL COMMENT '问题',
`keywords` varchar(500) DEFAULT NULL COMMENT '关键词,多个用逗号分隔',
`answer` text COMMENT '标准回答',
`category` varchar(100) DEFAULT NULL COMMENT '分类',
`sort_number` int(11) DEFAULT 0 COMMENT '排序号',
`status` tinyint(1) DEFAULT 0 COMMENT '状态 0正常 1禁用',
`user_id` int(11) DEFAULT NULL COMMENT '创建用户ID',
`deleted` tinyint(1) DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`faq_id`),
KEY `idx_house_faq_status` (`status`),
KEY `idx_house_faq_sort` (`sort_number`),
KEY `idx_house_faq_tenant` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI找房常见问题';
INSERT INTO `house_faq` (`question`, `keywords`, `answer`, `category`, `sort_number`, `status`)
VALUES
('怎么预约看房?', '预约看房,带看,流程', '在房源详情页点击预约看房,填写联系人和电话后提交即可,我们会尽快安排带看。', '看房流程', 1, 0),
('签约一般需要准备什么材料?', '签约,材料,合同', '通常需要准备身份证明、联系方式、企业主体资料(如为公司租赁)等,具体以顾问通知为准。', '签约流程', 2, 0),
('租房押金和佣金怎么收?', '押金,佣金,费用', '押金、佣金标准会根据具体房源和签约方式有所不同,您可以先告诉我目标房源或预算,我会优先帮您筛选合适房源,再由顾问说明费用细节。', '费用说明', 3, 0);

View File

@@ -0,0 +1,19 @@
CREATE TABLE `house_message` (
`message_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`real_name` varchar(100) NOT NULL COMMENT '姓名',
`phone` varchar(30) DEFAULT NULL COMMENT '手机号',
`wechat` varchar(100) DEFAULT NULL COMMENT '微信号',
`source` varchar(50) DEFAULT 'ai_house' COMMENT '来源',
`comments` varchar(500) DEFAULT NULL COMMENT '备注',
`status` tinyint(1) DEFAULT 0 COMMENT '状态 0未处理 1已处理',
`deleted` tinyint(1) DEFAULT 0 COMMENT '是否删除, 0否, 1是',
`tenant_id` int(11) DEFAULT NULL COMMENT '租户ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`message_id`),
KEY `idx_house_message_user` (`user_id`),
KEY `idx_house_message_status` (`status`),
KEY `idx_house_message_tenant` (`tenant_id`),
KEY `idx_house_message_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI找房留言';

Binary file not shown.