Toggle navigation
Toggle navigation
This project
Loading...
Sign in
jiujian
/
client-customer-order-api
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
chen.yinxiang
2026-01-30 13:16:55 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Committed by
Jiaqi Xia
2026-02-06 10:52:06 +0800
Commit
0c0a8cda55e5e446ffc0f8ac7631ab66160b1fae
0c0a8cda
1 parent
26896180
ft:订阅消息创建接口
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
255 additions
and
0 deletions
src/main/java/com/infoloop/tianting/controller/MealOrderController.java
src/main/java/com/infoloop/tianting/service/MealOrderService.java
src/main/java/com/infoloop/tianting/service/client/MealOrderServiceRpcClient.java
src/main/java/com/infoloop/tianting/service/impl/MealOrderServiceImpl.java
src/main/proto/MealOrderService.proto
src/main/java/com/infoloop/tianting/controller/MealOrderController.java
View file @
0c0a8cd
...
...
@@ -4,7 +4,9 @@ import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import
com.infoloop.tianting.annotation.RepeatSubmit
;
import
com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse
;
import
com.infoloop.tianting.model.common.CreatedResult
;
import
com.infoloop.tianting.model.common.ResponseResult
;
import
com.infoloop.tianting.model.dto.MealOrderDTO
;
import
com.infoloop.tianting.model.dto.OrderSubscriptionMessageDTO
;
import
com.infoloop.tianting.model.vo.DeliveryRuleVO
;
import
com.infoloop.tianting.model.vo.DinerMealSuspensionRecordVO
;
import
com.infoloop.tianting.model.vo.MealOrderVO
;
...
...
@@ -75,4 +77,14 @@ public class MealOrderController {
return
mealOrderService
.
queryDinerMealSuspensionRecordsByCondition
();
}
@ApiOperation
(
value
=
"小程序:创建订餐订阅消息授权记录"
)
@PutMapping
(
"/mealorder/subscriptionmessage"
)
@ResponseStatus
(
HttpStatus
.
CREATED
)
public
ResponseResult
<
Boolean
>
createOrderSubscriptionMessage
(
@RequestBody
@Valid
final
OrderSubscriptionMessageDTO
.
CreateOrderSubscriptionMessageDTO
dto
)
{
mealOrderService
.
createOrderSubscriptionMessage
(
dto
);
return
ResponseResult
.
ok
(
true
);
}
}
...
...
src/main/java/com/infoloop/tianting/service/MealOrderService.java
View file @
0c0a8cd
...
...
@@ -3,6 +3,7 @@ package com.infoloop.tianting.service;
import
com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse
;
import
com.infoloop.tianting.model.common.CreatedResult
;
import
com.infoloop.tianting.model.dto.MealOrderDTO
;
import
com.infoloop.tianting.model.dto.OrderSubscriptionMessageDTO
;
import
com.infoloop.tianting.model.vo.DeliveryRuleVO
;
import
com.infoloop.tianting.model.vo.DinerMealSuspensionRecordVO
;
import
com.infoloop.tianting.model.vo.MealOrderVO
;
...
...
@@ -30,4 +31,6 @@ public interface MealOrderService {
* @return 停餐信息列表
*/
List
<
DinerMealSuspensionRecordVO
>
queryDinerMealSuspensionRecordsByCondition
();
void
createOrderSubscriptionMessage
(
OrderSubscriptionMessageDTO
.
CreateOrderSubscriptionMessageDTO
dto
);
}
...
...
src/main/java/com/infoloop/tianting/service/client/MealOrderServiceRpcClient.java
View file @
0c0a8cd
...
...
@@ -12,6 +12,7 @@ import com.infoloop.tianting.mealorderservice.CreateMpAccountDinerRefRpcRequest;
import
com.infoloop.tianting.mealorderservice.CreateMpAccountDinerRefRpcResponse
;
import
com.infoloop.tianting.mealorderservice.CreateMpAccountRpcRequest
;
import
com.infoloop.tianting.mealorderservice.CreateMpAccountRpcResponse
;
import
com.infoloop.tianting.mealorderservice.CreateOrderSubscriptionMessageRpcRequest
;
import
com.infoloop.tianting.mealorderservice.DeleteDinersByIdsRpcRequest
;
import
com.infoloop.tianting.mealorderservice.DeleteMpAccountDinerRefsByIdsRpcRequest
;
import
com.infoloop.tianting.mealorderservice.DinerCreation
;
...
...
@@ -302,4 +303,29 @@ public class MealOrderServiceRpcClient {
return
mealOrderServiceRpcBlockingStub
.
queryDinerMealSuspensionRecordsByCondition
(
request
.
build
())
.
getResponsesList
();
}
/**
* 小程序订阅消息:创建一次性授权记录(待使用)
*/
public
long
createOrderSubscriptionMessage
(
int
enterpriseId
,
int
dinerId
,
String
openId
,
String
templateId
,
long
orderPeriodStartDate
,
@Nullable
String
jumpPath
,
@Nullable
Long
orderPeriodEndDate
)
{
final
var
req
=
CreateOrderSubscriptionMessageRpcRequest
.
newBuilder
()
.
setEnterpriseId
(
enterpriseId
)
.
setDinerId
(
dinerId
)
.
setOpenId
(
openId
)
.
setTemplateId
(
templateId
)
.
setOrderPeriodStartDate
(
orderPeriodStartDate
)
.
setJumpPath
(
jumpPath
==
null
?
""
:
jumpPath
);
if
(
orderPeriodEndDate
!=
null
)
{
req
.
setOrderPeriodEndDate
(
orderPeriodEndDate
);
}
return
mealOrderServiceRpcBlockingStub
.
createOrderSubscriptionMessage
(
req
.
build
()).
getId
();
}
}
...
...
src/main/java/com/infoloop/tianting/service/impl/MealOrderServiceImpl.java
View file @
0c0a8cd
...
...
@@ -7,6 +7,7 @@ import com.infoloop.tianting.exception.ErrorCodeEnum;
import
com.infoloop.tianting.mealorderservice.*
;
import
com.infoloop.tianting.model.common.CreatedResult
;
import
com.infoloop.tianting.model.dto.MealOrderDTO
;
import
com.infoloop.tianting.model.dto.OrderSubscriptionMessageDTO
;
import
com.infoloop.tianting.model.vo.DeliveryRuleVO
;
import
com.infoloop.tianting.model.vo.DinerMealSuspensionRecordVO
;
import
com.infoloop.tianting.model.vo.MealOrderVO
;
...
...
@@ -19,12 +20,14 @@ import com.infoloop.tianting.service.SuspensionService;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Service
;
import
java.time.DayOfWeek
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.time.LocalTime
;
import
java.time.ZoneId
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Date
;
...
...
@@ -33,6 +36,8 @@ import java.util.Map;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
import
static
com
.
infoloop
.
tianting
.
constant
.
ConfigConstants
.
MINI_PROGRAM_ORDER_REMINDER_TEMPLATE_ID
;
@Slf4j
@Service
@RequiredArgsConstructor
(
onConstructor
=
@__
(
@Autowired
))
...
...
@@ -46,6 +51,9 @@ public class MealOrderServiceImpl implements MealOrderService {
private
final
SuspensionService
suspensionService
;
@Value
(
MINI_PROGRAM_ORDER_REMINDER_TEMPLATE_ID
)
private
String
templateId
;
@Override
public
List
<
MealOrderVO
>
queryMealOrders
(
MealOrderDTO
.
QueryMealOrderDTO
queryMealOrderDTO
)
{
final
var
loginInfo
=
LoginContextHolder
.
getLoginInfo
();
...
...
@@ -293,4 +301,152 @@ public class MealOrderServiceImpl implements MealOrderService {
* 将RPC响应转换为VO对象
*/
// 已改为统一从 SuspensionService 获取并集窗口,不再需要单条转换
@Override
public
void
createOrderSubscriptionMessage
(
OrderSubscriptionMessageDTO
.
CreateOrderSubscriptionMessageDTO
dto
)
{
final
var
enterpriseId
=
LoginContextHolder
.
getEnterpriseId
();
final
var
openId
=
LoginContextHolder
.
getOpenId
();
if
(
openId
==
null
||
openId
.
isBlank
())
{
throw
new
IllegalStateException
(
"当前登录用户 openId 为空,无法创建订阅提醒"
);
}
if
(
dto
.
getJumpPath
()
==
null
||
dto
.
getJumpPath
().
isBlank
())
{
throw
new
IllegalArgumentException
(
"jumpPath 不能为空"
);
}
// 1. 处理就餐人 ID:如果为空,从关联关系推断
int
dinerId
=
dto
.
getDinerId
()
!=
null
?
dto
.
getDinerId
()
:
getDefaultDinerId
(
enterpriseId
,
openId
);
// 2. 处理模板 ID:如果为空,使用配置的默认值
String
finalTemplateId
=
(
dto
.
getTemplateId
()
!=
null
&&
!
dto
.
getTemplateId
().
isBlank
())
?
dto
.
getTemplateId
()
:
templateId
;
if
(
finalTemplateId
==
null
||
finalTemplateId
.
isBlank
())
{
throw
new
IllegalStateException
(
"订阅消息模板ID未配置,无法创建订阅提醒"
);
}
// 3. 先根据订餐规则配置,计算本次"订餐周期"的开始/结束时间
final
var
period
=
resolveOrderPeriod
(
enterpriseId
);
// 打印所有参数,用于检查库服务版本是否匹配
log
.
info
(
"创建订阅消息 - 调用库服务参数: enterpriseId={}, dinerId={}, openId={}, templateId={}, orderPeriodStartDate={}, jumpPath={}, orderPeriodEndDate={}"
,
enterpriseId
,
dinerId
,
openId
,
finalTemplateId
,
period
.
startAt
,
dto
.
getJumpPath
(),
period
.
endAt
);
mealOrderServiceRpcClient
.
createOrderSubscriptionMessage
(
enterpriseId
,
dinerId
,
openId
,
finalTemplateId
,
period
.
startAt
,
dto
.
getJumpPath
(),
period
.
endAt
);
}
/**
* 如果前端未传就餐人 ID,从当前用户的关联关系中推断默认值
* 如果只有一个关联的就餐人,返回该就餐人 ID;如果有多个或没有,返回 0
*/
private
int
getDefaultDinerId
(
int
enterpriseId
,
String
openId
)
{
final
var
loginInfo
=
LoginContextHolder
.
getLoginInfo
();
final
var
mpAccountDinerRefs
=
mealOrderServiceRpcClient
.
queryMpAccountDinerRefsByCondition
(
enterpriseId
,
Collections
.
singletonList
(
loginInfo
.
getId
()),
Collections
.
emptyList
()
);
if
(
mpAccountDinerRefs
.
size
()
==
1
)
{
return
mpAccountDinerRefs
.
get
(
0
).
getDinerId
();
}
return
0
;
}
private
static
final
class
Period
{
final
long
startAt
;
final
Long
endAt
;
private
Period
(
long
startAt
,
Long
endAt
)
{
this
.
startAt
=
startAt
;
this
.
endAt
=
endAt
;
}
}
/**
* 根据订餐规则配置推导"最近的未来订餐周期"的开始/结束时间。
*
* 规则说明:
* - 使用 DeliveryRuleServiceRpc.getDefaultDeliveryTemplateAndRule 的 configJson.rules[0]
* - 该规则包含:startTime / endTime(HH:mm:ss)以及 monday~sunday 开关
* - 从"当前时刻"开始,向未来查找最近的一个启用日 + startTime 作为周期开始时间
* - 周期结束时间暂定为:该周最后一个启用日 + endTime(如果滚到了下一周,则用下一周的最后启用日)
*/
private
Period
resolveOrderPeriod
(
int
enterpriseId
)
{
final
var
response
=
deliveryRuleServiceRpcClient
.
getDefaultDeliveryTemplateAndRule
(
enterpriseId
);
final
var
rule
=
response
.
getRule
();
if
(
rule
==
null
||
rule
.
getConfigJson
().
getRulesList
().
isEmpty
())
{
throw
new
IllegalStateException
(
"订餐规则未配置,无法创建订阅提醒"
);
}
final
var
cfg
=
rule
.
getConfigJson
().
getRules
(
0
);
final
String
startTimeStr
=
cfg
.
getStartTime
();
final
String
endTimeStr
=
cfg
.
getEndTime
();
if
(
startTimeStr
==
null
||
endTimeStr
==
null
)
{
throw
new
IllegalStateException
(
"订餐规则时间配置不完整,无法创建订阅提醒"
);
}
final
List
<
DayOfWeek
>
enabledDays
=
new
ArrayList
<>();
if
(
cfg
.
getMonday
())
enabledDays
.
add
(
DayOfWeek
.
MONDAY
);
if
(
cfg
.
getTuesday
())
enabledDays
.
add
(
DayOfWeek
.
TUESDAY
);
if
(
cfg
.
getWednesday
())
enabledDays
.
add
(
DayOfWeek
.
WEDNESDAY
);
if
(
cfg
.
getThursday
())
enabledDays
.
add
(
DayOfWeek
.
THURSDAY
);
if
(
cfg
.
getFriday
())
enabledDays
.
add
(
DayOfWeek
.
FRIDAY
);
if
(
cfg
.
getSaturday
())
enabledDays
.
add
(
DayOfWeek
.
SATURDAY
);
if
(
cfg
.
getSunday
())
enabledDays
.
add
(
DayOfWeek
.
SUNDAY
);
if
(
enabledDays
.
isEmpty
())
{
throw
new
IllegalStateException
(
"订餐规则未启用任何星期,无法创建订阅提醒"
);
}
final
var
startTime
=
LocalTime
.
parse
(
startTimeStr
,
DateUtil
.
HH_MM_SS
);
final
var
endTime
=
LocalTime
.
parse
(
endTimeStr
,
DateUtil
.
HH_MM_SS
);
final
ZoneId
zone
=
DateUtil
.
CHINA_ZONE
;
final
var
now
=
java
.
time
.
ZonedDateTime
.
now
(
zone
);
// 从当前时间起,向后最多检查 14 天,找到最近的"启用日 + startTime"
LocalDate
periodStartDate
=
null
;
DayOfWeek
lastEnabledDay
=
Collections
.
max
(
enabledDays
);
for
(
int
i
=
0
;
i
<
14
;
i
++)
{
LocalDate
candidateDate
=
now
.
toLocalDate
().
plusDays
(
i
);
DayOfWeek
dow
=
candidateDate
.
getDayOfWeek
();
if
(!
enabledDays
.
contains
(
dow
))
{
continue
;
}
var
candidateStart
=
candidateDate
.
atTime
(
startTime
).
atZone
(
zone
);
if
(!
candidateStart
.
isBefore
(
now
))
{
periodStartDate
=
candidateDate
;
break
;
}
}
if
(
periodStartDate
==
null
)
{
throw
new
IllegalStateException
(
"根据订餐规则无法找到未来的订餐周期开始时间"
);
}
// 周期结束时间:使用"同一周内最后一个启用日 + endTime"
// 这里以 periodStartDate 所在周的周一为基准
LocalDate
monday
=
periodStartDate
.
with
(
DayOfWeek
.
MONDAY
);
LocalDate
lastEnabledDate
=
monday
.
plusDays
(
lastEnabledDay
.
getValue
()
-
1L
);
var
endDateTime
=
lastEnabledDate
.
atTime
(
endTime
).
atZone
(
zone
);
long
startMs
=
periodStartDate
.
atTime
(
startTime
).
atZone
(
zone
).
toInstant
().
toEpochMilli
();
long
endMs
=
endDateTime
.
toInstant
().
toEpochMilli
();
return
new
Period
(
startMs
,
endMs
);
}
}
...
...
src/main/proto/MealOrderService.proto
View file @
0c0a8cd
...
...
@@ -63,6 +63,12 @@ service MealOrderServiceRpc {
rpc
batchCreateDinerMealSuspensionRecords
(
BatchCreateDinerMealSuspensionRecordsRpcRequest
)
returns
(
BatchCreateDinerMealSuspensionRecordsRpcResponse
);
rpc
updateDinerMealSuspensionRecord
(
UpdateDinerMealSuspensionRecordRpcRequest
)
returns
(
UpdateDinerMealSuspensionRecordRpcResponse
);
rpc
deleteDinerMealSuspensionRecordsByIds
(
DeleteDinerMealSuspensionRecordsByIdsRpcRequest
)
returns
(
DeleteDinerMealSuspensionRecordsRpcResponse
);
// 订餐订阅消息相关服务
rpc
CreateOrderSubscriptionMessage
(
CreateOrderSubscriptionMessageRpcRequest
)
returns
(
CreateOrderSubscriptionMessageRpcResponse
)
{}
rpc
GetUserOrderSubscriptionMessageHistory
(
GetUserOrderSubscriptionMessageHistoryRpcRequest
)
returns
(
GetUserOrderSubscriptionMessageHistoryRpcResponse
)
{}
rpc
BatchDeleteOrderSubscriptionMessagesByIds
(
BatchDeleteOrderSubscriptionMessagesByIdsRpcRequest
)
returns
(
BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse
)
{}
rpc
BatchPhysicalDeleteOrderSubscriptionMessagesByIds
(
BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcRequest
)
returns
(
BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse
)
{}
}
enum
MealOrderOrderMethodEnum
{
...
...
@@ -955,3 +961,55 @@ message BatchCreateDinerMealSuspensionRecordsRpcResponse {
bool
isCreated
=
1
;
repeated
int32
ids
=
2
;
}
// 订餐订阅消息相关消息定义
message
CreateOrderSubscriptionMessageRpcRequest
{
int32
enterpriseId
=
1
;
int32
dinerId
=
2
;
string
openId
=
3
;
string
templateId
=
4
;
int64
orderPeriodStartDate
=
5
;
// Unix 时间戳(毫秒),精确到秒
string
jumpPath
=
6
;
// 跳转路径(小程序 path),可选
int64
orderPeriodEndDate
=
7
;
// Unix 时间戳(毫秒),精确到秒,可选
}
message
CreateOrderSubscriptionMessageRpcResponse
{
int64
id
=
1
;
}
message
GetUserOrderSubscriptionMessageHistoryRpcRequest
{
int32
enterpriseId
=
1
;
string
openId
=
2
;
int64
orderPeriodStartDate
=
3
;
// Unix 时间戳(毫秒),精确到秒,可选,如果传入则查询大于等于该值的记录
}
message
OrderSubscriptionMessageRpcResponse
{
int64
id
=
1
;
int64
orderPeriodStartDate
=
2
;
// Unix 时间戳(毫秒),精确到秒
int64
createdAt
=
3
;
// Unix 时间戳(毫秒)
bool
isDeleted
=
4
;
string
jumpPath
=
5
;
// 跳转路径(小程序 path),可选
int64
orderPeriodEndDate
=
6
;
// Unix 时间戳(毫秒),精确到秒,可选
}
message
GetUserOrderSubscriptionMessageHistoryRpcResponse
{
repeated
OrderSubscriptionMessageRpcResponse
response
=
1
;
}
message
BatchDeleteOrderSubscriptionMessagesByIdsRpcRequest
{
int32
enterpriseId
=
1
;
repeated
int64
ids
=
2
;
// 要删除的记录ID列表
}
message
BatchDeleteOrderSubscriptionMessagesByIdsRpcResponse
{
int32
affectedRows
=
1
;
// 受影响的行数
}
message
BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcRequest
{
int32
enterpriseId
=
1
;
repeated
int64
ids
=
2
;
// 要删除的记录ID列表
}
message
BatchPhysicalDeleteOrderSubscriptionMessagesByIdsRpcResponse
{
int32
affectedRows
=
1
;
// 受影响的行数
}
...
...
Please
register
or
login
to post a comment