日报填报日期范围修改

main
kangwenjing 2026-07-22 18:01:16 +08:00
parent f3966a45ed
commit 4eed47cc70
6 changed files with 121 additions and 27 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -103,6 +103,7 @@ public class WorkServiceImpl implements WorkService {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
private static final DateTimeFormatter REPORT_DATE_TIME_TEXT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm");
private static final ZoneId BUSINESS_ZONE_ID = ZoneId.of("Asia/Shanghai");
private static final LocalTime DAILY_REPORT_CUTOFF_TIME = LocalTime.of(10, 0);
private final WorkMapper workMapper;
private final OpportunityMapper opportunityMapper;
@ -2465,7 +2466,12 @@ public class WorkServiceImpl implements WorkService {
}
private LocalDate currentBusinessDate() {
return LocalDate.now(BUSINESS_ZONE_ID);
return resolveBusinessDate(LocalDateTime.now(BUSINESS_ZONE_ID));
}
static LocalDate resolveBusinessDate(LocalDateTime businessDateTime) {
LocalDate date = businessDateTime.toLocalDate();
return businessDateTime.toLocalTime().isBefore(DAILY_REPORT_CUTOFF_TIME) ? date.minusDays(1) : date;
}
private record PhotoMetadata(String cleanText, List<String> photoUrls) {}

View File

@ -12,25 +12,21 @@ minio:
spring:
application:
name: unis-crm-backend
jackson:
time-zone: Asia/Shanghai
servlet:
multipart:
max-file-size: 500MB
max-request-size: 600MB
datasource:
url: jdbc:postgresql://127.0.0.1:5432/unis_crm
url: jdbc:postgresql://192.168.124.202:5432/unis_crm
username: postgres
password: 199628
password: unis@123
driver-class-name: org.postgresql.Driver
hikari:
connection-init-sql: SET TIME ZONE 'Asia/Shanghai'
data:
redis:
host: 127.0.0.1
host: 192.168.124.202
port: 6379
password: 199628@tlw
database: 14
password: zghz@123
database: 12
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml

View File

@ -4,6 +4,14 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.unis.crm.mapper.WorkMapper">
<sql id="dailyReportBusinessDateExpression">
case
when r.submit_time is not null and (r.submit_time at time zone 'Asia/Shanghai')::time &lt; time '10:00'
then (r.submit_time at time zone 'Asia/Shanghai')::date - 1
else coalesce((r.submit_time at time zone 'Asia/Shanghai')::date, r.report_date)
end
</sql>
<sql id="checkInExportFilters">
<if test="startDate != null">
and c.checkin_date &gt;= #{startDate}
@ -34,10 +42,22 @@
<sql id="dailyReportExportFilters">
<if test="startDate != null">
and r.report_date &gt;= #{startDate}
and (
case
when r.submit_time is not null and (r.submit_time at time zone 'Asia/Shanghai')::time &lt; time '10:00'
then (r.submit_time at time zone 'Asia/Shanghai')::date - 1
else coalesce((r.submit_time at time zone 'Asia/Shanghai')::date, r.report_date)
end
) &gt;= #{startDate}
</if>
<if test="endDate != null">
and r.report_date &lt;= #{endDate}
and (
case
when r.submit_time is not null and (r.submit_time at time zone 'Asia/Shanghai')::time &lt; time '10:00'
then (r.submit_time at time zone 'Asia/Shanghai')::date - 1
else coalesce((r.submit_time at time zone 'Asia/Shanghai')::date, r.report_date)
end
) &lt;= #{endDate}
</if>
<if test="keyword != null and keyword != ''">
and (
@ -320,7 +340,7 @@
select
r.id,
'日报' as type,
to_char(r.report_date, 'YYYY-MM-DD') as date,
to_char(<include refid="dailyReportBusinessDateExpression"/>, 'YYYY-MM-DD') as date,
to_char(r.submit_time, 'HH24:MI') as time,
case
when coalesce(nullif(btrim(u.display_name), ''), nullif(btrim(u.username), '')) is not null
@ -343,7 +363,11 @@
end as status,
rc.score,
rc.comment_content as comment,
coalesce(r.report_date::timestamp + r.submit_time::time, r.created_at) as sort_time
coalesce(
(<include refid="dailyReportBusinessDateExpression"/>)::timestamp
+ (r.submit_time at time zone 'Asia/Shanghai')::time,
r.created_at
) as sort_time
from work_daily_report r
left join sys_user u on u.user_id = r.user_id and u.is_deleted = 0
left join (
@ -362,7 +386,7 @@
select
r.id,
'日报' as type,
to_char(r.report_date, 'YYYY-MM-DD') as date,
to_char(<include refid="dailyReportBusinessDateExpression"/>, 'YYYY-MM-DD') as date,
to_char(r.submit_time, 'HH24:MI') as time,
case
when coalesce(nullif(btrim(u.display_name), ''), nullif(btrim(u.username), '')) is not null
@ -385,7 +409,11 @@
end as status,
rc.score,
rc.comment_content as comment,
coalesce(r.report_date::timestamp + r.submit_time::time, r.created_at) as sort_time
coalesce(
(<include refid="dailyReportBusinessDateExpression"/>)::timestamp
+ (r.submit_time at time zone 'Asia/Shanghai')::time,
r.created_at
) as sort_time
from work_daily_report r
left join sys_user u on u.user_id = r.user_id and u.is_deleted = 0
left join (
@ -408,7 +436,7 @@
select
r.id,
'日报' as type,
to_char(r.report_date, 'YYYY-MM-DD') as date,
to_char(<include refid="dailyReportBusinessDateExpression"/>, 'YYYY-MM-DD') as date,
to_char(r.submit_time, 'HH24:MI') as time,
case
when coalesce(nullif(btrim(u.display_name), ''), nullif(btrim(u.username), '')) is not null
@ -431,7 +459,11 @@
end as status,
rc.score,
rc.comment_content as comment,
coalesce(r.report_date::timestamp + r.submit_time::time, r.created_at) as sort_time
coalesce(
(<include refid="dailyReportBusinessDateExpression"/>)::timestamp
+ (r.submit_time at time zone 'Asia/Shanghai')::time,
r.created_at
) as sort_time
from work_daily_report r
left join sys_user u on u.user_id = r.user_id and u.is_deleted = 0
left join (
@ -459,7 +491,7 @@
select
r.id,
'日报' as type,
to_char(r.report_date, 'YYYY-MM-DD') as date,
to_char(<include refid="dailyReportBusinessDateExpression"/>, 'YYYY-MM-DD') as date,
to_char(r.submit_time, 'HH24:MI') as time,
case
when coalesce(nullif(btrim(u.display_name), ''), nullif(btrim(u.username), '')) is not null
@ -482,7 +514,11 @@
end as status,
rc.score,
rc.comment_content as comment,
coalesce(r.report_date::timestamp + r.submit_time::time, r.created_at) as sort_time
coalesce(
(<include refid="dailyReportBusinessDateExpression"/>)::timestamp
+ (r.submit_time at time zone 'Asia/Shanghai')::time,
r.created_at
) as sort_time
from work_daily_report r
left join sys_user u on u.user_id = r.user_id and u.is_deleted = 0
left join (
@ -573,7 +609,14 @@
<select id="selectDailyReportExportRows" resultType="com.unis.crm.dto.work.WorkDailyReportExportDTO">
select
r.id,
to_char(r.report_date, 'YYYY-MM-DD') as reportDate,
to_char(
case
when r.submit_time is not null and (r.submit_time at time zone 'Asia/Shanghai')::time &lt; time '10:00'
then (r.submit_time at time zone 'Asia/Shanghai')::date - 1
else coalesce((r.submit_time at time zone 'Asia/Shanghai')::date, r.report_date)
end,
'YYYY-MM-DD'
) as reportDate,
to_char(r.submit_time, 'YYYY-MM-DD HH24:MI') as submitTime,
coalesce(nullif(btrim(u.display_name), ''), nullif(btrim(u.username), ''), '') as userName,
coalesce(nullif(btrim(org_info.org_names), ''), '') as deptName,
@ -618,7 +661,14 @@
<select id="selectDailyReportExportRowsByUserIds" resultType="com.unis.crm.dto.work.WorkDailyReportExportDTO">
select
r.id,
to_char(r.report_date, 'YYYY-MM-DD') as reportDate,
to_char(
case
when r.submit_time is not null and (r.submit_time at time zone 'Asia/Shanghai')::time &lt; time '10:00'
then (r.submit_time at time zone 'Asia/Shanghai')::date - 1
else coalesce((r.submit_time at time zone 'Asia/Shanghai')::date, r.report_date)
end,
'YYYY-MM-DD'
) as reportDate,
to_char(r.submit_time, 'YYYY-MM-DD HH24:MI') as submitTime,
coalesce(nullif(btrim(u.display_name), ''), nullif(btrim(u.username), ''), '') as userName,
coalesce(nullif(btrim(org_info.org_names), ''), '') as deptName,

View File

@ -21,6 +21,7 @@ import com.unis.crm.dto.work.WorkReportLineItemRequest;
import com.unis.crm.dto.work.WorkTomorrowPlanItemRequest;
import com.unis.crm.mapper.OpportunityMapper;
import java.time.LocalDate;
import java.time.LocalDateTime;
import com.unis.crm.mapper.ProfileMapper;
import com.unis.crm.mapper.WorkMapper;
import com.unis.crm.service.CrmDataVisibilityService;
@ -718,6 +719,20 @@ class WorkServiceImplTest {
eq(17L));
}
@Test
void resolveBusinessDate_shouldUsePreviousDateBeforeTenOClock() {
assertEquals(
LocalDate.of(2026, 7, 21),
WorkServiceImpl.resolveBusinessDate(LocalDateTime.of(2026, 7, 22, 9, 59, 59)));
}
@Test
void resolveBusinessDate_shouldUseCurrentDateFromTenOClock() {
assertEquals(
LocalDate.of(2026, 7, 22),
WorkServiceImpl.resolveBusinessDate(LocalDateTime.of(2026, 7, 22, 10, 0)));
}
@Test
void saveDailyReport_shouldNotSyncOpportunitySnapshotForNonSalesRole() {
CreateWorkDailyReportRequest request = new CreateWorkDailyReportRequest();

View File

@ -219,9 +219,30 @@ const FALLBACK_OPPORTUNITY_TYPE_OPTIONS = [
{ value: "替换", label: "替换" },
] as const;
function getCurrentReportDate(now = new Date()): string {
const parts = new Intl.DateTimeFormat("en-CA", {
timeZone: "Asia/Shanghai",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
hourCycle: "h23",
}).formatToParts(now);
const values = Object.fromEntries(parts.map((part) => [part.type, part.value]));
const reportDate = new Date(Date.UTC(Number(values.year), Number(values.month) - 1, Number(values.day)));
if (Number(values.hour) < 10) {
reportDate.setUTCDate(reportDate.getUTCDate() - 1);
}
return [
reportDate.getUTCFullYear(),
String(reportDate.getUTCMonth() + 1).padStart(2, "0"),
String(reportDate.getUTCDate()).padStart(2, "0"),
].join("-");
}
function createEmptyReportLine(): WorkReportLineItem {
return {
workDate: format(new Date(), "yyyy-MM-dd"),
workDate: getCurrentReportDate(),
bizType: "sales",
bizId: "",
bizName: "",
@ -790,7 +811,7 @@ export default function Work() {
const isMobileViewport = useIsMobileViewport();
const isWecomInternalBrowser = useIsWecomBrowser();
const disableMobileMotion = isMobileViewport || isWecomInternalBrowser;
const currentWorkDate = format(new Date(), "yyyy-MM-dd");
const currentWorkDate = getCurrentReportDate();
const hasAutoRefreshedLocation = useRef(false);
const photoInputRef = useRef<HTMLInputElement | null>(null);
const historyLoadMoreRef = useRef<HTMLDivElement | null>(null);
@ -3767,7 +3788,11 @@ function HistoryCard({
</div>
<div>
<h3 className="text-sm font-semibold text-slate-900 dark:text-white">{item.type}</h3>
<p className="text-[10px] text-slate-500 dark:text-slate-400">{[item.date, item.time].filter(Boolean).join(" ")}</p>
<p className="text-[10px] text-slate-500 dark:text-slate-400">
{item.type === "日报"
? `日报日期:${item.date || "未标注日期"}`
: [item.date, item.time].filter(Boolean).join(" ")}
</p>
</div>
</div>
<div className="flex flex-col items-end gap-1">
@ -4330,7 +4355,7 @@ function ReportPanel({
</div>
<div className="flex w-full items-center justify-between gap-2 lg:w-auto lg:justify-end">
<span className="crm-pill crm-pill-neutral text-[11px]">
{currentWorkDate}
{currentWorkDate}
</span>
<button
type="button"
@ -4715,7 +4740,9 @@ function HistoryDetailModal({
<div className="flex-1 overflow-y-auto px-4 py-4 pb-[calc(env(safe-area-inset-bottom)+20px)] sm:px-6 sm:py-5">
<div>
<p className="text-xs font-medium text-slate-400 dark:text-slate-500">
{[item.date, item.time].filter(Boolean).join(" ") || "无时间信息"}
{item.type === "日报"
? `日报日期:${item.date || "未标注日期"}`
: [item.date, item.time].filter(Boolean).join(" ") || "无时间信息"}
</p>
</div>