diff --git a/dl_admin/ruoyi-admin/pom.xml b/dl_admin/ruoyi-admin/pom.xml index 6595d34..cbfa14f 100644 --- a/dl_admin/ruoyi-admin/pom.xml +++ b/dl_admin/ruoyi-admin/pom.xml @@ -133,6 +133,12 @@ spring-websocket 5.3.31 + + + org.lionsoul + ip2region + 2.7.0 + diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/controller/WebController.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/controller/WebController.java index 08f6ead..6d8561c 100644 --- a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/controller/WebController.java +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/controller/WebController.java @@ -3,14 +3,18 @@ package com.ruoyi.base.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.base.domain.BaseInquiry; import com.ruoyi.base.domain.BasePic; import com.ruoyi.base.domain.BaseSiteInfo; +import com.ruoyi.base.service.IBaseInquiryService; import com.ruoyi.base.service.IBasePicService; import com.ruoyi.base.service.IBaseSiteInfoService; import com.ruoyi.busi.domain.BusiCategory; +import com.ruoyi.busi.domain.BusiInquiryItem; import com.ruoyi.busi.domain.BusiProdNew; import com.ruoyi.busi.service.IBusiCategoryService; import com.ruoyi.busi.service.IBusiProdNewService; +import com.ruoyi.busi.utils.CommonUtils; import com.ruoyi.busi.vo.BusiCategoryVO; import com.ruoyi.busi.vo.ProdNewVO; import com.ruoyi.busi.vo.WebDetailVO; @@ -27,8 +31,11 @@ import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.HttpRequestHandler; import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.*; @@ -48,11 +55,15 @@ public class WebController extends BaseController { @Autowired private IBasePicService basePicService; @Autowired + private IBaseInquiryService baseInquiryService; + @Autowired private IBusiCategoryService categoryService; @Autowired private IBaseSiteInfoService siteInfoService; @Autowired private IBusiProdNewService prodNewService; + @Autowired + private CommonUtils commonUtils; /** * 导航栏接口--所有分类 @@ -286,4 +297,43 @@ public class WebController extends BaseController { Page page = new Page<>(pageNum,pageSize); return R.ok(prodNewService.searchTextAll(tenantId,text,page)); } + + /** + * 查询盘设置-都有哪些字段需要填写,是否必填 + * @author vinjor-M + * @date 10:04 2025/7/8 + * @return com.ruoyi.common.core.domain.AjaxResult + **/ + @ApiOperation("查询盘设置-都有哪些字段需要填写,是否必填") + @ApiImplicitParam(name = "tenantId", value = "站点唯一码", required = true, dataType = "string", paramType = "query", dataTypeClass = String.class) + @GetMapping("/inquirySet") + public R inquirySet(@RequestParam(required = true) String tenantId, HttpServletRequest request){ + String ip = CommonUtils.getIpAddr(request); + System.out.println(ip); + System.out.println(CommonUtils.getAddr(ip)); + return R.ok(baseInquiryService.getInquiry(tenantId)); + } + + /** + * 提交在线询盘表单 + * @author vinjor-M + * @date 10:04 2025/7/8 + * @return com.ruoyi.common.core.domain.AjaxResult + **/ + @ApiOperation("提交在线询盘表单") + @ApiImplicitParams(value = { + @ApiImplicitParam(name = "companyName", value = "公司名称", required = false, paramType = "body"), + @ApiImplicitParam(name = "name", value = "姓名", required = false, paramType = "body"), + @ApiImplicitParam(name = "tel", value = "电话或WhatsApp", required = false, paramType = "body"), + @ApiImplicitParam(name = "title", value = "标题", required = false, paramType = "body"), + @ApiImplicitParam(name = "content", value = "内容", required = true, paramType = "body"), + @ApiImplicitParam(name = "email", value = "email", required = false, paramType = "body"), + @ApiImplicitParam(name = "tenantId", value = "公司名称", required = true, paramType = "body") + }) + @PostMapping("/inquirySave") + public R inquirySave(@ApiIgnore @RequestBody BusiInquiryItem inquiryItem, HttpServletRequest request){ + String ip = CommonUtils.getIpAddr(request); + System.out.println(CommonUtils.getAddr(ip)); + return R.ok(); + } } diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/domain/BaseInquiry.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/domain/BaseInquiry.java index 0b2f875..939d1d7 100644 --- a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/domain/BaseInquiry.java +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/base/domain/BaseInquiry.java @@ -4,6 +4,8 @@ import com.ruoyi.common.annotation.Excel; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; import lombok.*; import com.ruoyi.common.core.domain.DlBaseEntity; @@ -20,52 +22,74 @@ import com.ruoyi.common.core.domain.DlBaseEntity; @Builder @NoArgsConstructor @AllArgsConstructor +@ApiModel(value = "BaseInquiry", description = "询盘设置实体") public class BaseInquiry extends DlBaseEntity { private static final long serialVersionUID = 1L; /** 主键 */ @TableId(type = IdType.ASSIGN_UUID) + @ApiModelProperty("主键") private String id; /** 是否开启填写公司名称 */ @Excel(name = "公司名称") + @ApiModelProperty("公司名称字段是否显示") private Boolean company; /** 公司名称是否必填 */ @Excel(name = "公司名称是否必填") + @ApiModelProperty("公司名称是否必填") private Boolean companyMust; + /** 是否开启填写email */ + @Excel(name = "是否开启填写email") + @ApiModelProperty("email字段是否显示") + private Boolean email; + + /** email是否必填 */ + @Excel(name = "email是否必填") + @ApiModelProperty("email字段是否必填") + private Boolean emailMust; + /** 是否开启填写电话/WhatsApp */ @Excel(name = "电话/WhatsApp") + @ApiModelProperty("电话/WhatsApp字段是否显示") private Boolean tel; /** 是否必填 */ @Excel(name = "是否必填") + @ApiModelProperty("电话/WhatsApp字段是否必填") private Boolean telMust; /** 是否开启填写标题 */ @Excel(name = "标题") + @ApiModelProperty("标题字段是否显示") private Boolean title; /** 是否必填 */ @Excel(name = "是否必填") + @ApiModelProperty("标题字段是否必填") private Boolean titleMust; /** 是否开启填写姓名 */ @Excel(name = "姓名") + @ApiModelProperty("姓名字段是否显示") private Boolean name; /** 是否必填 */ @Excel(name = "是否必填") + @ApiModelProperty("姓名字段是否必填") private Boolean nameMust; /** 提示文字(内容) */ @Excel(name = "提示文字", readConverterExp = "内=容") + @ApiModelProperty("提示文字(以placeholder形式显示在多行文本框中,要求输入内容时,这个提示以其他方式显示在多行文本框上方或下方)") private String content; /** 站点唯一编码(租户id) */ @Excel(name = "站点唯一编码", readConverterExp = "租=户id") + @ApiModelProperty("站点唯一编码") private String tenantId; } diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/controller/BusiInquiryItemController.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/controller/BusiInquiryItemController.java new file mode 100644 index 0000000..1a8dbce --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/controller/BusiInquiryItemController.java @@ -0,0 +1,112 @@ +package com.ruoyi.busi.controller; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.busi.domain.BusiInquiryItem; +import com.ruoyi.busi.service.IBusiInquiryItemService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 在线询盘记录Controller + * + * @author vinjor-m + * @date 2025-07-10 + */ +@RestController +@RequestMapping("/busi/inquiryItem") +public class BusiInquiryItemController extends BaseController +{ + @Autowired + private IBusiInquiryItemService busiInquiryItemService; + + /** + * 查询在线询盘记录列表 + */ + @PreAuthorize("@ss.hasPermi('busi:inquiryItem:list')") + @GetMapping("/list") + public AjaxResult list(BusiInquiryItem busiInquiryItem, + @RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) + { + Page page = new Page<>(pageNum, pageSize); + IPage list = busiInquiryItemService.queryListPage(busiInquiryItem,page); + return success(list); + } + + /** + * 导出在线询盘记录列表 + */ + @PreAuthorize("@ss.hasPermi('busi:inquiryItem:export')") + @Log(title = "在线询盘记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, BusiInquiryItem busiInquiryItem) + { + List list = busiInquiryItemService.list(); + ExcelUtil util = new ExcelUtil(BusiInquiryItem.class); + util.exportExcel(response, list, "在线询盘记录数据"); + } + + /** + * 获取在线询盘记录详细信息 + */ + @PreAuthorize("@ss.hasPermi('busi:inquiryItem:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") String id) + { + return success(busiInquiryItemService.getById(id)); + } + + /** + * 新增在线询盘记录 + */ + @PreAuthorize("@ss.hasPermi('busi:inquiryItem:add')") + @Log(title = "在线询盘记录", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody BusiInquiryItem busiInquiryItem) + { + return toAjax(busiInquiryItemService.save(busiInquiryItem)); + } + + /** + * 修改在线询盘记录 + */ + @PreAuthorize("@ss.hasPermi('busi:inquiryItem:edit')") + @Log(title = "在线询盘记录", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody BusiInquiryItem busiInquiryItem) + { + return toAjax(busiInquiryItemService.updateById(busiInquiryItem)); + } + + /** + * 删除在线询盘记录 + */ + @PreAuthorize("@ss.hasPermi('busi:inquiryItem:remove')") + @Log(title = "在线询盘记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable String[] ids) + { + List list = new ArrayList<>(Arrays.asList(ids)); + return toAjax(busiInquiryItemService.removeByIds(list)); + } +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/domain/BusiInquiryItem.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/domain/BusiInquiryItem.java new file mode 100644 index 0000000..9128b66 --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/domain/BusiInquiryItem.java @@ -0,0 +1,88 @@ +package com.ruoyi.busi.domain; + +import com.ruoyi.common.annotation.Excel; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import com.ruoyi.common.core.domain.DlBaseEntity; + +/** + * 在线询盘记录对象 dl_busi_inquiry_item + * + * @author vinjor-m + * @date 2025-07-10 + */ +@TableName("dl_busi_inquiry_item") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(value = "BusiInquiryItem", description = "在线询盘实体") +public class BusiInquiryItem extends DlBaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** 公司名称 */ + @Excel(name = "公司名称") + @ApiModelProperty("公司名称") + private String companyName; + + /** 姓名 */ + @Excel(name = "姓名") + @ApiModelProperty("姓名") + private String name; + + /** 电话 */ + @Excel(name = "电话") + @ApiModelProperty("电话") + private String tel; + + /** 标题 */ + @Excel(name = "标题") + @ApiModelProperty("标题") + private String title; + + /** 内容 */ + @Excel(name = "内容") + @ApiModelProperty("内容") + private String content; + + /** 电子邮件 */ + @Excel(name = "电子邮件") + @ApiModelProperty("电子邮件") + private String email; + + /** 来源IP */ + @Excel(name = "来源IP") + @ApiModelProperty("来源IP") + private String ip; + + /** 来源国家 */ + @Excel(name = "来源国家") + @ApiModelProperty("来源国家") + private String national; + + /** 洲 */ + @Excel(name = "洲") + @ApiModelProperty("洲") + private String oceania; + + /** 页面路径 */ + @Excel(name = "页面路径") + private String pageUrl; + + /** 站点唯一编码(租户id) */ + @Excel(name = "站点唯一编码", readConverterExp = "租=户id") + @ApiModelProperty("站点唯一编码") + private String tenantId; + +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/mapper/BusiInquiryItemMapper.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/mapper/BusiInquiryItemMapper.java new file mode 100644 index 0000000..e7b27b0 --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/mapper/BusiInquiryItemMapper.java @@ -0,0 +1,21 @@ +package com.ruoyi.busi.mapper; + +import java.util.List; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.busi.domain.BusiInquiryItem; +import org.apache.ibatis.annotations.Param; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 在线询盘记录Mapper接口 + * + * @author vinjor-m + * @date 2025-07-10 + */ +@Mapper +public interface BusiInquiryItemMapper extends BaseMapper +{ + IPage queryListPage(@Param("entity") BusiInquiryItem entity, Page page); +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/service/IBusiInquiryItemService.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/service/IBusiInquiryItemService.java new file mode 100644 index 0000000..e46991e --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/service/IBusiInquiryItemService.java @@ -0,0 +1,18 @@ +package com.ruoyi.busi.service; + +import java.util.List; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.busi.domain.BusiInquiryItem; + +/** + * 在线询盘记录Service接口 + * + * @author vinjor-m + * @date 2025-07-10 + */ +public interface IBusiInquiryItemService extends IService +{ + IPage queryListPage(BusiInquiryItem pageReqVO, Page page); +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/service/impl/BusiInquiryItemServiceImpl.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/service/impl/BusiInquiryItemServiceImpl.java new file mode 100644 index 0000000..fbd9356 --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/service/impl/BusiInquiryItemServiceImpl.java @@ -0,0 +1,30 @@ +package com.ruoyi.busi.service.impl; + +import java.util.List; +import com.ruoyi.common.utils.DateUtils; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.busi.mapper.BusiInquiryItemMapper; +import com.ruoyi.busi.domain.BusiInquiryItem; +import com.ruoyi.busi.service.IBusiInquiryItemService; + +/** + * 在线询盘记录Service业务层处理 + * + * @author vinjor-m + * @date 2025-07-10 + */ +@Service +public class BusiInquiryItemServiceImpl extends ServiceImpl implements IBusiInquiryItemService +{ + @Autowired + private BusiInquiryItemMapper busiInquiryItemMapper; + + @Override + public IPage queryListPage(BusiInquiryItem pageReqVO, Page page) { + return busiInquiryItemMapper.queryListPage(pageReqVO, page); + } +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/utils/CommonUtils.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/utils/CommonUtils.java new file mode 100644 index 0000000..4b9a5f1 --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/utils/CommonUtils.java @@ -0,0 +1,79 @@ +package com.ruoyi.busi.utils; +import cn.hutool.core.util.StrUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.util.IOUtils; +import org.lionsoul.ip2region.xdb.Searcher; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; +import java.util.stream.Collectors; +/** + * 通用方法 + * @author 马文杰 + * @version 1.0 + */ +@Component +@Slf4j +public class CommonUtils { + + private static final String UNKNOWN = "unknown"; + + /** + * 获取 IP地址 + * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址, + * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } + + @SneakyThrows + public static String getAddr(String ip){ + ClassPathResource resource = new ClassPathResource("ip2region.xdb"); + InputStream inputStream = resource.getInputStream(); + String dbPath = "src/main/resources/ip2region.xdb"; + // 1、从 dbPath 加载整个 xdb 到内存。 + byte[] cBuff; + try { + cBuff = IOUtils.toByteArray(inputStream); + } catch (Exception e) { + log.info("failed to load content from %s: %s\n", dbPath, e); + return null; + } + + // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 + Searcher searcher; + try { + searcher = Searcher.newWithBuffer(cBuff); + } catch (Exception e) { + log.info("failed to create content cached searcher: %s\n", e); + return null; + } + // 3、查询 + try { + String region = searcher.search(ip); + return region; + } catch (Exception e) { + log.info("failed to search(%s): %s\n", ip, e); + } + return null; + } + +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/utils/NoticeUtils.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/utils/NoticeUtils.java deleted file mode 100644 index 0737c58..0000000 --- a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/busi/utils/NoticeUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.ruoyi.busi.utils; -import cn.hutool.core.util.StrUtil; -import org.springframework.stereotype.Component; - -import java.util.*; -import java.util.stream.Collectors; -/** - * 文本相似度对比 - * @author 朱春云 - * @version 1.0 - */ -@Component -public class NoticeUtils { - - /** - * 比较两个文本字符串的相似度 - * @param str1 文本1 - * @param str2 文本2 - * @return 相似度值,范围在0到1之间,值越大相似度越高 - */ - public static double computeJaccardSimilarity(String str1, String str2) { - if (str1 == null || str2 == null || (str1.isEmpty() && str2.isEmpty())) { - return 1.0; // 空字符串认为完全相同 - } - if (str1.isEmpty() || str2.isEmpty()) { - return 0.0; // 一个为空另一个非空,认为完全不同 - } - - Set set1 = Arrays.stream(str1.split("\\s+")).collect(Collectors.toSet()); - Set set2 = Arrays.stream(str2.split("\\s+")).collect(Collectors.toSet()); - - Set intersection = new HashSet<>(set1); - intersection.retainAll(set2); - - Set union = new HashSet<>(set1); - union.addAll(set2); // 正确的并集 - - if (union.isEmpty()) { - return 0.0; // 安全处理,避免除以零 - } - - return (double) intersection.size() / union.size(); - } - - /** - * 翻译通告的博主类型字典 - * @author vinjor-M - * @date 17:10 2025/3/25 - * @return java.lang.String - **/ - public String translateBloggerTypes(String bloggerTypes, Map categoryMap){ - List rtnList = new ArrayList<>(); - List bloggerTypeList = Arrays.asList(bloggerTypes.split(StrUtil.COMMA)); - bloggerTypeList.forEach(item->{ - if(categoryMap.get(item)!=null){ - rtnList.add(categoryMap.get(item)); - } - }); - return String.join(" ",rtnList); - } - - -} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/xdb/Header.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/xdb/Header.java new file mode 100644 index 0000000..c1c6839 --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/xdb/Header.java @@ -0,0 +1,38 @@ +// Copyright 2022 The Ip2Region Authors. All rights reserved. +// Use of this source code is governed by a Apache2.0-style +// license that can be found in the LICENSE file. +// @Author Lion +// @Date 2022/06/23 + +package com.ruoyi.xdb; + +import org.lionsoul.ip2region.xdb.Searcher; + +public class Header { + public final int version; + public final int indexPolicy; + public final int createdAt; + public final int startIndexPtr; + public final int endIndexPtr; + public final byte[] buffer; + + public Header(byte[] buff) { + assert buff.length >= 16; + version = Searcher.getInt2(buff, 0); + indexPolicy = Searcher.getInt2(buff, 2); + createdAt = Searcher.getInt(buff, 4); + startIndexPtr = Searcher.getInt(buff, 8); + endIndexPtr = Searcher.getInt(buff, 12); + buffer = buff; + } + + @Override public String toString() { + return "{" + + "Version: " + version + ',' + + "IndexPolicy: " + indexPolicy + ',' + + "CreatedAt: " + createdAt + ',' + + "StartIndexPtr: " + startIndexPtr + ',' + + "EndIndexPtr: " + endIndexPtr + + '}'; + } +} diff --git a/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/xdb/Searcher.java b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/xdb/Searcher.java new file mode 100644 index 0000000..6bc6512 --- /dev/null +++ b/dl_admin/ruoyi-admin/src/main/java/com/ruoyi/xdb/Searcher.java @@ -0,0 +1,281 @@ +// Copyright 2022 The Ip2Region Authors. All rights reserved. +// Use of this source code is governed by a Apache2.0-style +// license that can be found in the LICENSE file. + +package com.ruoyi.xdb; + +// xdb searcher (Not thread safe implementation) +// @Author Lion +// @Date 2022/06/23 + + +import org.lionsoul.ip2region.xdb.Header; + +import java.io.IOException; +import java.io.RandomAccessFile; + +public class Searcher { + // constant defined copied from the xdb maker + public static final int HeaderInfoLength = 256; + public static final int VectorIndexRows = 256; + public static final int VectorIndexCols = 256; + public static final int VectorIndexSize = 8; + public static final int SegmentIndexSize = 14; + + // random access file handle for file based search + private final RandomAccessFile handle; + + private int ioCount = 0; + + // vector index. + // use the byte[] instead of VectorIndex entry array to keep + // the minimal memory allocation. + private final byte[] vectorIndex; + + // xdb content buffer, used for in-memory search + private final byte[] contentBuff; + + // --- static method to create searchers + + public static Searcher newWithFileOnly(String dbPath) throws IOException { + return new Searcher(dbPath, null, null); + } + + public static Searcher newWithVectorIndex(String dbPath, byte[] vectorIndex) throws IOException { + return new Searcher(dbPath, vectorIndex, null); + } + + public static Searcher newWithBuffer(byte[] cBuff) throws IOException { + return new Searcher(null, null, cBuff); + } + + // --- End of creator + + public Searcher(String dbFile, byte[] vectorIndex, byte[] cBuff) throws IOException { + if (cBuff != null) { + this.handle = null; + this.vectorIndex = null; + this.contentBuff = cBuff; + } else { + this.handle = new RandomAccessFile(dbFile, "r"); + this.vectorIndex = vectorIndex; + this.contentBuff = null; + } + } + + public void close() throws IOException { + if (this.handle != null) { + this.handle.close(); + } + } + + public int getIOCount() { + return ioCount; + } + + public String search(String ipStr) throws Exception { + long ip = checkIP(ipStr); + return search(ip); + } + + public String search(long ip) throws IOException { + // reset the global counter + this.ioCount = 0; + + // locate the segment index block based on the vector index + long sPtr = 0, ePtr = 0; + int il0 = (int) ((ip >> 24) & 0xFF); + int il1 = (int) ((ip >> 16) & 0xFF); + int idx = il0 * VectorIndexCols * VectorIndexSize + il1 * VectorIndexSize; + // System.out.printf("il0: %d, il1: %d, idx: %d\n", il0, il1, idx); + if (vectorIndex != null) { + sPtr = getIntLong(vectorIndex, idx); + ePtr = getIntLong(vectorIndex, idx + 4); + } else if (contentBuff != null) { + sPtr = getIntLong(contentBuff, HeaderInfoLength + idx); + ePtr = getIntLong(contentBuff, HeaderInfoLength + idx + 4); + } else { + final byte[] buff = new byte[VectorIndexSize]; + read(HeaderInfoLength + idx, buff); + sPtr = getIntLong(buff, 0); + ePtr = getIntLong(buff, 4); + } + + // System.out.printf("sPtr: %d, ePtr: %d\n", sPtr, ePtr); + + // binary search the segment index block to get the region info + final byte[] buff = new byte[SegmentIndexSize]; + int dataLen = -1; + long dataPtr = -1, l = 0, h = (ePtr - sPtr) / SegmentIndexSize; + while (l <= h) { + long m = (l + h) >> 1; + long p = sPtr + m * SegmentIndexSize; + + // read the segment index + read(p, buff); + long sip = getIntLong(buff, 0); + if (ip < sip) { + h = m - 1; + } else { + long eip = getIntLong(buff, 4); + if (ip > eip) { + l = m + 1; + } else { + dataLen = getInt2(buff, 8); + dataPtr = getIntLong(buff, 10); + break; + } + } + } + + // empty match interception + // System.out.printf("dataLen: %d, dataPtr: %d\n", dataLen, dataPtr); + if (dataPtr < 0) { + return null; + } + + // load and return the region data + final byte[] regionBuff = new byte[dataLen]; + read(dataPtr, regionBuff); + return new String(regionBuff, "utf-8"); + } + + protected void read(long offset, byte[] buffer) throws IOException { + // check the in-memory buffer first + if (contentBuff != null) { + // @TODO: reduce data copying, directly decode the data ? + // @TODO: added by Leon at 2025/06/10, when offset is negative and the content byte is not going to work. + // we need a better solution for the content buffer which is greater than (2^31 - 1 << 2) + int int_idx = (int) offset; + if (int_idx < 0) { + throw new IOException("No content buffer policy for NOW since the xdb is too large, use file or vectorIndex instead"); + } + + System.arraycopy(contentBuff, int_idx, buffer, 0, buffer.length); + return; + } + + // read from the file handle + assert handle != null; + handle.seek(offset); + + this.ioCount++; + int rLen = handle.read(buffer); + if (rLen != buffer.length) { + throw new IOException("incomplete read: read bytes should be " + buffer.length); + } + } + + // --- static cache util function + + public static org.lionsoul.ip2region.xdb.Header loadHeader(RandomAccessFile handle) throws IOException { + handle.seek(0); + final byte[] buff = new byte[HeaderInfoLength]; + handle.read(buff); + return new org.lionsoul.ip2region.xdb.Header(buff); + } + + public static org.lionsoul.ip2region.xdb.Header loadHeaderFromFile(String dbPath) throws IOException { + final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); + final Header header = loadHeader(handle); + handle.close(); + return header; + } + + public static byte[] loadVectorIndex(RandomAccessFile handle) throws IOException { + handle.seek(HeaderInfoLength); + int len = VectorIndexRows * VectorIndexCols * VectorIndexSize; + final byte[] buff = new byte[len]; + int rLen = handle.read(buff); + if (rLen != len) { + throw new IOException("incomplete read: read bytes should be " + len); + } + + return buff; + } + + public static byte[] loadVectorIndexFromFile(String dbPath) throws IOException { + final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); + final byte[] vIndex = loadVectorIndex(handle); + handle.close(); + return vIndex; + } + + public static byte[] loadContent(RandomAccessFile handle) throws IOException { + handle.seek(0); + final byte[] buff = new byte[(int) handle.length()]; + int rLen = handle.read(buff); + if (rLen != buff.length) { + throw new IOException("incomplete read: read bytes should be " + buff.length); + } + + return buff; + } + + public static byte[] loadContentFromFile(String dbPath) throws IOException { + final RandomAccessFile handle = new RandomAccessFile(dbPath, "r"); + final byte[] content = loadContent(handle); + handle.close(); + return content; + } + + // --- End cache load util function + + // --- static util method + + /* get an int from a byte array start from the specified offset */ + public static long getIntLong(byte[] b, int offset) { + return ( + ((b[offset++] & 0x000000FFL)) | + ((b[offset++] << 8) & 0x0000FF00L) | + ((b[offset++] << 16) & 0x00FF0000L) | + ((b[offset ] << 24) & 0xFF000000L) + ); + } + + public static int getInt(byte[] b, int offset) { + return ( + ((b[offset++] & 0x000000FF)) | + ((b[offset++] << 8) & 0x0000FF00) | + ((b[offset++] << 16) & 0x00FF0000) | + ((b[offset ] << 24) & 0xFF000000) + ); + } + + public static int getInt2(byte[] b, int offset) { + return ( + ((b[offset++] & 0x000000FF)) | + ((b[offset ] << 8) & 0x0000FF00) + ); + } + + /* long int to ip string */ + public static String long2ip( long ip ) + { + return String.valueOf((ip >> 24) & 0xFF) + '.' + + ((ip >> 16) & 0xFF) + '.' + ((ip >> 8) & 0xFF) + '.' + ((ip) & 0xFF); + } + + public static final byte[] shiftIndex = {24, 16, 8, 0}; + + /* check the specified ip address */ + public static long checkIP(String ip) throws Exception { + String[] ps = ip.split("\\."); + if (ps.length != 4) { + throw new Exception("invalid ip address `" + ip + "`"); + } + + long ipDst = 0; + for (int i = 0; i < ps.length; i++) { + int val = Integer.parseInt(ps[i]); + if (val > 255) { + throw new Exception("ip part `"+ps[i]+"` should be less then 256"); + } + + ipDst |= ((long) val << shiftIndex[i]); + } + + return ipDst & 0xFFFFFFFFL; + } + +} \ No newline at end of file diff --git a/dl_admin/ruoyi-admin/src/main/resources/ip2region.xdb b/dl_admin/ruoyi-admin/src/main/resources/ip2region.xdb new file mode 100644 index 0000000..7052c05 Binary files /dev/null and b/dl_admin/ruoyi-admin/src/main/resources/ip2region.xdb differ diff --git a/dl_admin/ruoyi-admin/src/main/resources/ip2region/ip2region.xdb b/dl_admin/ruoyi-admin/src/main/resources/ip2region/ip2region.xdb new file mode 100644 index 0000000..7052c05 Binary files /dev/null and b/dl_admin/ruoyi-admin/src/main/resources/ip2region/ip2region.xdb differ diff --git a/dl_admin/ruoyi-admin/src/main/resources/mapper/base/BaseInquiryMapper.xml b/dl_admin/ruoyi-admin/src/main/resources/mapper/base/BaseInquiryMapper.xml index cb813a3..b4ffd1f 100644 --- a/dl_admin/ruoyi-admin/src/main/resources/mapper/base/BaseInquiryMapper.xml +++ b/dl_admin/ruoyi-admin/src/main/resources/mapper/base/BaseInquiryMapper.xml @@ -8,6 +8,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + @@ -24,7 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, company, company_must, tel, tel_must, title, title_must, name, name_must, content, tenant_id, creator, create_time, updater, update_time, del_flag from dl_base_inquiry + select id, company, company_must,email,email_must, tel, tel_must, title, title_must, name, name_must, content, tenant_id, creator, create_time, updater, update_time, del_flag from dl_base_inquiry + + + and company_name like concat('%', #{entity.companyName}, '%') + and name like concat('%', #{entity.name}, '%') + and tel = #{entity.tel} + and title = #{entity.title} + and content = #{entity.content} + and email = #{entity.email} + and ip = #{entity.ip} + and national = #{entity.national} + and oceania = #{entity.oceania} + and page_url = #{entity.pageUrl} + and tenant_id = #{entity.tenantId} + + + \ No newline at end of file diff --git a/dl_vue/src/api/busi/inquiryItem.js b/dl_vue/src/api/busi/inquiryItem.js new file mode 100644 index 0000000..c55c4fc --- /dev/null +++ b/dl_vue/src/api/busi/inquiryItem.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询在线询盘记录列表 +export function listInquiryItem(query) { + return request({ + url: '/busi/inquiryItem/list', + method: 'get', + params: query + }) +} + +// 查询在线询盘记录详细 +export function getInquiryItem(id) { + return request({ + url: '/busi/inquiryItem/' + id, + method: 'get' + }) +} + +// 新增在线询盘记录 +export function addInquiryItem(data) { + return request({ + url: '/busi/inquiryItem', + method: 'post', + data: data + }) +} + +// 修改在线询盘记录 +export function updateInquiryItem(data) { + return request({ + url: '/busi/inquiryItem', + method: 'put', + data: data + }) +} + +// 删除在线询盘记录 +export function delInquiryItem(id) { + return request({ + url: '/busi/inquiryItem/' + id, + method: 'delete' + }) +} diff --git a/dl_vue/src/views/base/inquiry/index.vue b/dl_vue/src/views/base/inquiry/index.vue index f51b4b4..6c988d5 100644 --- a/dl_vue/src/views/base/inquiry/index.vue +++ b/dl_vue/src/views/base/inquiry/index.vue @@ -8,6 +8,33 @@ + + + + + + + + + + + + + + @@ -35,6 +62,33 @@ + + + + + + + + + + + + + + @@ -89,33 +143,7 @@ - - - - - - - - - - - - - - + @@ -147,6 +175,8 @@ export default { id: null, company: null, companyMust: null, + email: null, + emailMust: null, tel: null, telMust: null, title: null, diff --git a/dl_vue/src/views/busi/inquiryItem/index.vue b/dl_vue/src/views/busi/inquiryItem/index.vue new file mode 100644 index 0000000..f85caba --- /dev/null +++ b/dl_vue/src/views/busi/inquiryItem/index.vue @@ -0,0 +1,382 @@ + + +