feat: add recruitment fair statistics API
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import com.ruoyi.cms.domain.vo.FairStatisticsFairItem;
|
||||
import com.ruoyi.cms.mapper.FairStatisticsMapper;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 后台招聘会数据统计。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/cms/fair-statistics")
|
||||
@Api(tags = "后台:招聘会数据统计")
|
||||
public class FairStatisticsController extends BaseController
|
||||
{
|
||||
private static final String ONLINE = "online";
|
||||
|
||||
private static final String OUTDOOR = "outdoor";
|
||||
|
||||
@Autowired
|
||||
private FairStatisticsMapper fairStatisticsMapper;
|
||||
|
||||
/**
|
||||
* 按单场招聘会或按招聘会起始时间范围统计。
|
||||
*
|
||||
* @param fairType 招聘会类型:online(线上)或 outdoor(户外)
|
||||
* @param fairId 单场招聘会 ID;传该参数时不接受时间范围
|
||||
* @param startDate 按时间统计的起始日期(含当天)
|
||||
* @param endDate 按时间统计的结束日期(含当天)
|
||||
*/
|
||||
@ApiOperation("招聘会数据汇总统计")
|
||||
@PreAuthorize("@ss.hasAnyPermi('cms:publicJobFair:list,cms:outdoorFair:list')")
|
||||
@GetMapping("/summary")
|
||||
public AjaxResult summary(@RequestParam String fairType,
|
||||
@RequestParam(required = false) String fairId,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate)
|
||||
{
|
||||
String normalizedType = fairType == null ? "" : fairType.trim().toLowerCase();
|
||||
if (!ONLINE.equals(normalizedType) && !OUTDOOR.equals(normalizedType))
|
||||
{
|
||||
return AjaxResult.error("招聘会类型仅支持线上招聘会或户外招聘会");
|
||||
}
|
||||
|
||||
boolean hasFairId = StringUtils.isNotEmpty(fairId);
|
||||
boolean hasStartDate = startDate != null;
|
||||
boolean hasEndDate = endDate != null;
|
||||
if (hasFairId && (hasStartDate || hasEndDate))
|
||||
{
|
||||
return AjaxResult.error("单场统计与时间范围统计不能同时使用");
|
||||
}
|
||||
if (!hasFairId && (!hasStartDate || !hasEndDate))
|
||||
{
|
||||
return AjaxResult.error("请选择一场招聘会,或完整选择起始日期和结束日期");
|
||||
}
|
||||
|
||||
Date rangeStart = null;
|
||||
Date rangeEnd = null;
|
||||
if (!hasFairId)
|
||||
{
|
||||
LocalDate start = toLocalDate(startDate);
|
||||
LocalDate end = toLocalDate(endDate);
|
||||
if (end.isBefore(start))
|
||||
{
|
||||
return AjaxResult.error("结束日期不能早于起始日期");
|
||||
}
|
||||
rangeStart = toStartOfDay(start);
|
||||
// SQL 使用左闭右开区间,确保结束日期当天的招聘会被计入。
|
||||
rangeEnd = toStartOfDay(end.plusDays(1));
|
||||
}
|
||||
|
||||
List<FairStatisticsFairItem> fairs;
|
||||
if (ONLINE.equals(normalizedType))
|
||||
{
|
||||
fairs = fairStatisticsMapper.selectOnlineFairStatistics(fairId, rangeStart, rangeEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Long outdoorFairId = null;
|
||||
if (hasFairId)
|
||||
{
|
||||
try
|
||||
{
|
||||
outdoorFairId = Long.valueOf(fairId);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return AjaxResult.error("户外招聘会 ID 格式不正确");
|
||||
}
|
||||
}
|
||||
fairs = fairStatisticsMapper.selectOutdoorFairStatistics(outdoorFairId, rangeStart, rangeEnd);
|
||||
}
|
||||
|
||||
long companyCount = 0L;
|
||||
long jobCount = 0L;
|
||||
long demandCount = 0L;
|
||||
long interactionCount = 0L;
|
||||
for (FairStatisticsFairItem fair : fairs)
|
||||
{
|
||||
companyCount += numberOrZero(fair.getCompanyCount());
|
||||
jobCount += numberOrZero(fair.getJobCount());
|
||||
demandCount += numberOrZero(fair.getDemandCount());
|
||||
interactionCount += numberOrZero(fair.getInteractionCount());
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("fairType", normalizedType);
|
||||
result.put("fairCount", fairs.size());
|
||||
result.put("companyCount", companyCount);
|
||||
result.put("jobCount", jobCount);
|
||||
result.put("demandCount", demandCount);
|
||||
result.put("interactionCount", interactionCount);
|
||||
result.put("fairs", fairs);
|
||||
return AjaxResult.success(result);
|
||||
}
|
||||
|
||||
private LocalDate toLocalDate(Date value)
|
||||
{
|
||||
return value.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
|
||||
}
|
||||
|
||||
private Date toStartOfDay(LocalDate value)
|
||||
{
|
||||
return Date.from(value.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
private long numberOrZero(Long value)
|
||||
{
|
||||
return value == null ? 0L : value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.ruoyi.cms.domain.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 招聘会数据统计中的单场汇总项。
|
||||
*/
|
||||
public class FairStatisticsFairItem implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String fairId;
|
||||
|
||||
private String fairTitle;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startTime;
|
||||
|
||||
private Long companyCount;
|
||||
|
||||
private Long jobCount;
|
||||
|
||||
private Long demandCount;
|
||||
|
||||
/** 线上为投递岗位数,户外为签到数。 */
|
||||
private Long interactionCount;
|
||||
|
||||
public String getFairId()
|
||||
{
|
||||
return fairId;
|
||||
}
|
||||
|
||||
public void setFairId(String fairId)
|
||||
{
|
||||
this.fairId = fairId;
|
||||
}
|
||||
|
||||
public String getFairTitle()
|
||||
{
|
||||
return fairTitle;
|
||||
}
|
||||
|
||||
public void setFairTitle(String fairTitle)
|
||||
{
|
||||
this.fairTitle = fairTitle;
|
||||
}
|
||||
|
||||
public Date getStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(Date startTime)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public Long getCompanyCount()
|
||||
{
|
||||
return companyCount;
|
||||
}
|
||||
|
||||
public void setCompanyCount(Long companyCount)
|
||||
{
|
||||
this.companyCount = companyCount;
|
||||
}
|
||||
|
||||
public Long getJobCount()
|
||||
{
|
||||
return jobCount;
|
||||
}
|
||||
|
||||
public void setJobCount(Long jobCount)
|
||||
{
|
||||
this.jobCount = jobCount;
|
||||
}
|
||||
|
||||
public Long getDemandCount()
|
||||
{
|
||||
return demandCount;
|
||||
}
|
||||
|
||||
public void setDemandCount(Long demandCount)
|
||||
{
|
||||
this.demandCount = demandCount;
|
||||
}
|
||||
|
||||
public Long getInteractionCount()
|
||||
{
|
||||
return interactionCount;
|
||||
}
|
||||
|
||||
public void setInteractionCount(Long interactionCount)
|
||||
{
|
||||
this.interactionCount = interactionCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.ruoyi.cms.mapper;
|
||||
|
||||
import com.ruoyi.cms.domain.vo.FairStatisticsFairItem;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 招聘会数据统计查询。
|
||||
*/
|
||||
public interface FairStatisticsMapper
|
||||
{
|
||||
/** 查询线上招聘会的逐场统计。 */
|
||||
List<FairStatisticsFairItem> selectOnlineFairStatistics(@Param("fairId") String fairId,
|
||||
@Param("startTime") Date startTime,
|
||||
@Param("endTime") Date endTime);
|
||||
|
||||
/** 查询户外招聘会的逐场统计。 */
|
||||
List<FairStatisticsFairItem> selectOutdoorFairStatistics(@Param("fairId") Long fairId,
|
||||
@Param("startTime") Date startTime,
|
||||
@Param("endTime") Date endTime);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?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.ruoyi.cms.mapper.FairStatisticsMapper">
|
||||
|
||||
<!--
|
||||
线上招聘会:
|
||||
- 只统计招聘会类型为“1-线上”的有效公共招聘会;
|
||||
- 参会单位为审核通过的企业;
|
||||
- 职位、需求人数、投递岗位均限定为有效招聘会岗位;
|
||||
- vacancies 为空或非正数时按“若干”口径计为 1 人。
|
||||
-->
|
||||
<select id="selectOnlineFairStatistics" resultType="com.ruoyi.cms.domain.vo.FairStatisticsFairItem">
|
||||
SELECT
|
||||
f.job_fair_id AS "fairId",
|
||||
f.job_fair_title AS "fairTitle",
|
||||
f.job_fair_start_time AS "startTime",
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT company_relation.company_id)
|
||||
FROM public_job_fair_company company_relation
|
||||
WHERE company_relation.job_fair_id = f.job_fair_id
|
||||
AND company_relation.del_flag = '0'
|
||||
AND COALESCE(company_relation.review_status, '1') = '1'
|
||||
), 0) AS "companyCount",
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT fair_job.job_id)
|
||||
FROM public_job_fair_job fair_job
|
||||
INNER JOIN job job_info ON job_info.job_id = fair_job.job_id
|
||||
AND job_info.del_flag = '0'
|
||||
WHERE fair_job.job_fair_id = f.job_fair_id
|
||||
AND fair_job.del_flag = '0'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM public_job_fair_company company_relation
|
||||
WHERE company_relation.job_fair_id = fair_job.job_fair_id
|
||||
AND company_relation.company_id = fair_job.company_id
|
||||
AND company_relation.del_flag = '0'
|
||||
AND COALESCE(company_relation.review_status, '1') = '1'
|
||||
)
|
||||
), 0) AS "jobCount",
|
||||
COALESCE((
|
||||
SELECT SUM(CASE WHEN job_info.vacancies IS NULL OR job_info.vacancies <= 0 THEN 1 ELSE job_info.vacancies END)
|
||||
FROM (
|
||||
SELECT DISTINCT fair_job.job_id
|
||||
FROM public_job_fair_job fair_job
|
||||
WHERE fair_job.job_fair_id = f.job_fair_id
|
||||
AND fair_job.del_flag = '0'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM public_job_fair_company company_relation
|
||||
WHERE company_relation.job_fair_id = fair_job.job_fair_id
|
||||
AND company_relation.company_id = fair_job.company_id
|
||||
AND company_relation.del_flag = '0'
|
||||
AND COALESCE(company_relation.review_status, '1') = '1'
|
||||
)
|
||||
) fair_job
|
||||
INNER JOIN job job_info ON job_info.job_id = fair_job.job_id
|
||||
AND job_info.del_flag = '0'
|
||||
), 0) AS "demandCount",
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT fair_job.job_id)
|
||||
FROM public_job_fair_job fair_job
|
||||
INNER JOIN job job_info ON job_info.job_id = fair_job.job_id
|
||||
AND job_info.del_flag = '0'
|
||||
INNER JOIN job_apply apply_record ON apply_record.job_id = fair_job.job_id
|
||||
AND apply_record.del_flag = '0'
|
||||
WHERE fair_job.job_fair_id = f.job_fair_id
|
||||
AND fair_job.del_flag = '0'
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM public_job_fair_company company_relation
|
||||
WHERE company_relation.job_fair_id = fair_job.job_fair_id
|
||||
AND company_relation.company_id = fair_job.company_id
|
||||
AND company_relation.del_flag = '0'
|
||||
AND COALESCE(company_relation.review_status, '1') = '1'
|
||||
)
|
||||
), 0) AS "interactionCount"
|
||||
FROM public_job_fair f
|
||||
WHERE f.del_flag = '0'
|
||||
AND f.job_fair_type = '1'
|
||||
<choose>
|
||||
<when test="fairId != null and fairId != ''">
|
||||
AND f.job_fair_id = #{fairId}
|
||||
</when>
|
||||
<otherwise>
|
||||
AND f.job_fair_start_time <![CDATA[ >= ]]> #{startTime}
|
||||
AND f.job_fair_start_time <![CDATA[ < ]]> #{endTime}
|
||||
</otherwise>
|
||||
</choose>
|
||||
ORDER BY f.job_fair_start_time DESC NULLS LAST, f.job_fair_id DESC
|
||||
</select>
|
||||
|
||||
<!--
|
||||
户外招聘会没有投递入口,因此 interactionCount 只汇总签到人员记录。
|
||||
户外岗位也使用 job.vacancies;为空或非正数时按“若干=1 人”处理。
|
||||
-->
|
||||
<select id="selectOutdoorFairStatistics" resultType="com.ruoyi.cms.domain.vo.FairStatisticsFairItem">
|
||||
SELECT
|
||||
CAST(f.id AS VARCHAR) AS "fairId",
|
||||
f.title AS "fairTitle",
|
||||
f.hold_time AS "startTime",
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT company_relation.company_id)
|
||||
FROM fair_company company_relation
|
||||
WHERE company_relation.job_fair_id = f.id
|
||||
AND COALESCE(company_relation.del_flag, '0') = '0'
|
||||
), 0) AS "companyCount",
|
||||
COALESCE((
|
||||
SELECT COUNT(DISTINCT fair_job.job_id)
|
||||
FROM cms_outdoor_fair_job fair_job
|
||||
INNER JOIN job job_info ON job_info.job_id = fair_job.job_id
|
||||
AND job_info.del_flag = '0'
|
||||
WHERE fair_job.fair_id = f.id
|
||||
AND COALESCE(fair_job.del_flag, '0') = '0'
|
||||
), 0) AS "jobCount",
|
||||
COALESCE((
|
||||
SELECT SUM(CASE WHEN job_info.vacancies IS NULL OR job_info.vacancies <= 0 THEN 1 ELSE job_info.vacancies END)
|
||||
FROM (
|
||||
SELECT DISTINCT fair_job.job_id
|
||||
FROM cms_outdoor_fair_job fair_job
|
||||
WHERE fair_job.fair_id = f.id
|
||||
AND COALESCE(fair_job.del_flag, '0') = '0'
|
||||
) fair_job
|
||||
INNER JOIN job job_info ON job_info.job_id = fair_job.job_id
|
||||
AND job_info.del_flag = '0'
|
||||
), 0) AS "demandCount",
|
||||
COALESCE((
|
||||
SELECT COUNT(1)
|
||||
FROM cms_outdoor_fair_attendee attendee
|
||||
WHERE attendee.fair_id = f.id
|
||||
AND COALESCE(attendee.del_flag, '0') = '0'
|
||||
), 0) AS "interactionCount"
|
||||
FROM cms_outdoor_fair f
|
||||
WHERE COALESCE(f.del_flag, '0') = '0'
|
||||
<choose>
|
||||
<when test="fairId != null">
|
||||
AND f.id = #{fairId}
|
||||
</when>
|
||||
<otherwise>
|
||||
AND f.hold_time <![CDATA[ >= ]]> #{startTime}
|
||||
AND f.hold_time <![CDATA[ < ]]> #{endTime}
|
||||
</otherwise>
|
||||
</choose>
|
||||
ORDER BY f.hold_time DESC NULLS LAST, f.id DESC
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.ruoyi.cms.controller.cms;
|
||||
|
||||
import com.ruoyi.cms.domain.vo.FairStatisticsFairItem;
|
||||
import com.ruoyi.cms.mapper.FairStatisticsMapper;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class FairStatisticsControllerTest
|
||||
{
|
||||
@Test
|
||||
void onlineSingleFairAggregatesAllRequiredMetrics() throws Exception
|
||||
{
|
||||
FairStatisticsMapper mapper = mock(FairStatisticsMapper.class);
|
||||
when(mapper.selectOnlineFairStatistics(eq("online-100"), isNull(), isNull()))
|
||||
.thenReturn(Arrays.asList(
|
||||
item("online-100", 3L, 4L, 12L, 2L),
|
||||
item("online-101", 2L, 5L, 8L, 3L)));
|
||||
FairStatisticsController controller = controllerWith(mapper);
|
||||
|
||||
AjaxResult result = controller.summary("online", "online-100", null, null);
|
||||
|
||||
assertEquals(200, result.get(AjaxResult.CODE_TAG));
|
||||
Map<String, Object> data = dataOf(result);
|
||||
assertEquals("online", data.get("fairType"));
|
||||
assertEquals(2, data.get("fairCount"));
|
||||
assertEquals(5L, data.get("companyCount"));
|
||||
assertEquals(9L, data.get("jobCount"));
|
||||
assertEquals(20L, data.get("demandCount"));
|
||||
assertEquals(5L, data.get("interactionCount"));
|
||||
verify(mapper).selectOnlineFairStatistics("online-100", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void outdoorSingleFairRejectsNonNumericIdBeforeQuerying() throws Exception
|
||||
{
|
||||
FairStatisticsMapper mapper = mock(FairStatisticsMapper.class);
|
||||
FairStatisticsController controller = controllerWith(mapper);
|
||||
|
||||
AjaxResult result = controller.summary("outdoor", "not-a-number", null, null);
|
||||
|
||||
assertEquals(500, result.get(AjaxResult.CODE_TAG));
|
||||
assertEquals("户外招聘会 ID 格式不正确", result.get(AjaxResult.MSG_TAG));
|
||||
}
|
||||
|
||||
@Test
|
||||
void dateRangeUsesOutdoorMapperAndReturnsEmptyTotals() throws Exception
|
||||
{
|
||||
FairStatisticsMapper mapper = mock(FairStatisticsMapper.class);
|
||||
when(mapper.selectOutdoorFairStatistics(isNull(), any(Date.class), any(Date.class)))
|
||||
.thenReturn(Collections.emptyList());
|
||||
FairStatisticsController controller = controllerWith(mapper);
|
||||
|
||||
AjaxResult result = controller.summary("outdoor", null, new Date(1_700_000_000_000L), new Date(1_700_086_400_000L));
|
||||
|
||||
assertEquals(200, result.get(AjaxResult.CODE_TAG));
|
||||
Map<String, Object> data = dataOf(result);
|
||||
assertEquals("outdoor", data.get("fairType"));
|
||||
assertEquals(0, data.get("fairCount"));
|
||||
assertEquals(0L, data.get("companyCount"));
|
||||
assertEquals(0L, data.get("interactionCount"));
|
||||
verify(mapper).selectOutdoorFairStatistics(isNull(), any(Date.class), any(Date.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void incompleteScopeReturnsValidationError() throws Exception
|
||||
{
|
||||
FairStatisticsController controller = controllerWith(mock(FairStatisticsMapper.class));
|
||||
|
||||
AjaxResult result = controller.summary("online", null, new Date(), null);
|
||||
|
||||
assertEquals(500, result.get(AjaxResult.CODE_TAG));
|
||||
assertTrue(String.valueOf(result.get(AjaxResult.MSG_TAG)).contains("完整选择"));
|
||||
}
|
||||
|
||||
private FairStatisticsFairItem item(String fairId, Long companyCount, Long jobCount,
|
||||
Long demandCount, Long interactionCount)
|
||||
{
|
||||
FairStatisticsFairItem item = new FairStatisticsFairItem();
|
||||
item.setFairId(fairId);
|
||||
item.setCompanyCount(companyCount);
|
||||
item.setJobCount(jobCount);
|
||||
item.setDemandCount(demandCount);
|
||||
item.setInteractionCount(interactionCount);
|
||||
return item;
|
||||
}
|
||||
|
||||
private FairStatisticsController controllerWith(FairStatisticsMapper mapper) throws Exception
|
||||
{
|
||||
FairStatisticsController controller = new FairStatisticsController();
|
||||
Field field = FairStatisticsController.class.getDeclaredField("fairStatisticsMapper");
|
||||
field.setAccessible(true);
|
||||
field.set(controller, mapper);
|
||||
return controller;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> dataOf(AjaxResult result)
|
||||
{
|
||||
return (Map<String, Object>) result.get(AjaxResult.DATA_TAG);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user