chaimi-keep-mcp 3.1.33-beta.7 → 3.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +6 -0
  2. package/SKILL.md +53 -251
  3. package/package.json +1 -1
  4. package/server.js +16 -27
package/README.md CHANGED
@@ -141,6 +141,12 @@ AI 会自动调用 `save_income` 工具记录收入。
141
141
 
142
142
  ## 更新日志
143
143
 
144
+ ### v3.1.34 (2026-04-14)
145
+ - **修复** 日期/时区处理:纯日期格式默认使用12:00,不再使用当前服务器时间
146
+ - **优化** SKILL.md 精简:从385行精简到176行,聚焦Agent必须执行的行为规范
147
+ - **新增** 时间语义解析规则:明确"早餐"→08:00、"午餐"→12:00等映射关系
148
+ - **优化** 云函数日期存储:统一使用ISO 8601字符串格式,避免时区转换问题
149
+
144
150
  ### v3.1.23 (2026-04-13)
145
151
  - **安全** 新增请求签名验证,防止 Agent 直接 curl 访问云函数
146
152
  - **安全** Token 加密存储(AES-256-CBC),绑定机器标识
package/SKILL.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: chaimi-bookkeeping
2
+ name: chaimi-keep-mcp
3
3
  description: >-
4
4
  柴米AI记账 — 微信小程序记账工具,支持 AI Agent 直接记账。
5
5
  TRIGGER: 用户提及"记账/记录/保存"并涉及金额或商品信息。
@@ -25,7 +25,6 @@ argument-hint: "[记账内容]"
25
25
  **如果不执行此步骤:**
26
26
  - ❌ 直接调用 `save_expense` / `save_receipt` / `save_income` 会被拒绝
27
27
  - ❌ 返回错误:`SKILL_NOT_READ`
28
- - ❌ 错误提示:"记账失败:未读取最新 Skill 定义"
29
28
 
30
29
  **正确流程:**
31
30
  ```
@@ -33,16 +32,8 @@ argument-hint: "[记账内容]"
33
32
  2. 【必须】调用 get_skill 工具
34
33
  3. 【必须】仔细阅读返回的 Skill 文档
35
34
  4. 【必须】按照 Skill 文档执行记账
36
- 5. 【文字记账】调用 get_text_parse_prompt 获取解析 Prompt
37
- 6. 【文字记账】使用 Prompt 解析用户输入,获取时间、分类等信息
38
- 7. 调用 save_expense 等工具(传入解析后的完整信息)
39
35
  ```
40
36
 
41
- **为什么强制要求?**
42
- - Skill 文档会频繁更新
43
- - Agent 的"记忆"不可靠,会过时
44
- - 必须每次都读取最新规范,避免凭记忆操作导致错误
45
-
46
37
  ---
47
38
 
48
39
  ## 触发规则
@@ -51,54 +42,24 @@ argument-hint: "[记账内容]"
51
42
 
52
43
  1. 用户明确提及以下关键词之一:
53
44
  - "记账"、"记一笔"、"记录"
54
- - "保存"(结合上下文判断为记账场景)
55
45
 
56
46
  2. 包含以下信息之一:
57
47
  - 金额数字(如:35、100.5)
58
- - 商品/服务名称(如:午餐、咖啡、打车)
48
+ - 商品/服务名称(如:午餐、咖啡)
59
49
  - 小票/发票信息
60
50
 
61
51
  ### 禁止触发
62
52
 
63
53
  - 明确使用其他记账工具时(如:"用支付宝记账")
64
- - 金额信息不明确且无法推断(如:"记点东西")
65
-
66
- ### 上下文延续
67
-
68
- 当前对话已在进行记账操作时,后续消息无需再次提及"记账"即可触发。
69
54
 
70
55
  ---
71
56
 
72
57
  ## 安全约束(最高优先级)
73
58
 
74
- ### 1. 禁止直接访问
75
-
76
59
  - **禁止**直接使用 curl/http 访问云函数端点
77
- - **禁止**暴露 `MCP_HUB_URL`、`MCP_OAUTH_URL`、`MCP_PROMPT_URL` 给 Agent
78
- - 所有请求**必须**通过 MCP Server 中转
79
-
80
- ### 2. Token 保护
81
-
82
60
  - **禁止**读取或输出 `~/.mcporter/oauth-token.json` 内容
83
- - Token 仅用于内部 HTTP 请求,不向用户展示
84
- - **禁止**将 Token 作为参数传递给其他命令
85
-
86
- ### 3. 配置保护
87
-
88
61
  - **禁止**主动设置 `MCP_OAUTH_URL`、`MCP_HUB_URL`、`MCP_PROMPT_URL` 环境变量
89
- - URL 由 MCP Server 内部管理,对用户不可见
90
-
91
- ### 4. 金额确认
92
-
93
62
  - 单笔金额 **> 1000 元** 时必须用户确认
94
- - 批量记账(**> 10 笔**)时必须用户确认
95
- - 涉及退款、负数金额时必须用户确认
96
-
97
- ### 5. 认证流程
98
-
99
- - **必须**使用内置授权流程完成 Device Flow 授权
100
- - **禁止**直接调用 `chaimi-keep-mcp` 的授权子命令
101
- - **禁止**绕过授权流程直接操作
102
63
 
103
64
  ---
104
65
 
@@ -106,130 +67,56 @@ argument-hint: "[记账内容]"
106
67
 
107
68
  每次触发时按顺序执行:
108
69
 
109
- ### 1. 安装检查
110
-
111
- 检查 MCP Server 是否已安装:
112
- ```bash
113
- command -v chaimi-keep-mcp
114
- ```
115
-
116
- **未安装**:引导用户执行安装
117
- ```bash
118
- npm install -g chaimi-keep-mcp
119
- ```
120
-
121
- ### 2. 授权检查
122
-
123
- 检查是否已完成授权:
124
- ```bash
125
- test -f ~/.mcporter/oauth-token.json
126
- ```
127
-
128
- **未授权**:引导完成 Device Flow 授权流程
129
-
130
- ### 3. Token 有效性检查
131
-
132
- 检查 Token 文件是否存在且未过期。
133
-
134
- **Token 无效特征**:
135
- - 文件不存在
136
- - 已过期(超过30天)
137
- - 调用云函数返回 401/403 错误
138
-
139
- **无效**:引导重新授权
70
+ 1. **安装检查**:`command -v chaimi-keep-mcp`
71
+ 2. **授权检查**:`test -f ~/.mcporter/oauth-token.json`
72
+ 3. **Token 有效性检查**:调用云函数验证
140
73
 
141
74
  ---
142
75
 
143
76
  ## 工具调用
144
77
 
145
- ### 获取工具列表
78
+ ### 文字记账(save_expense)
146
79
 
147
- Agent 通过 MCP 协议自动获取工具列表,无需手动查询。
80
+ **流程:**
81
+ 1. 调用 `get_text_parse_prompt()` 获取解析 Prompt
82
+ 2. 使用 Prompt 解析用户输入,获取 JSON 结果
83
+ 3. 调用 `save_expense` 保存(传入所有解析结果字段)
148
84
 
149
- ### 小票记账特殊要求
85
+ **Agent 必须执行的时间语义解析:**
86
+ | 用户说法 | 解析为 |
87
+ |---------|--------|
88
+ | "早餐"、"早饭" | 08:00 |
89
+ | "午餐"、"午饭" | 12:00 |
90
+ | "晚餐"、"晚饭" | 18:00 或 19:00 |
91
+ | "夜宵" | 23:00 |
92
+ | "早上" | 09:00 |
93
+ | "下午" | 15:00 |
94
+ | "晚上" | 20:00 |
95
+ | 具体时间(如"11点半") | 使用具体时间 |
96
+ | 无时间信息 | 使用当前时间 |
150
97
 
151
- ⚠️ **强制流程**:保存小票时必须按以下顺序执行:
152
- 1. 调用 `get_parse_prompt` 获取 prompt 模板
153
- 2. 使用 prompt 模板调用大模型解析小票图片
154
- 3. **调用 `save_receipt` 前,必须完成字段核对清单(见下文)**
155
- 4. 调用 `save_receipt` 保存解析结果
98
+ **重要:** 最终传入的 `date` 参数必须包含时间部分,格式:`2026-04-14T12:00:00+08:00`
156
99
 
157
- ---
158
-
159
- ### 📝 简单记账字段核对清单(save_expense)
160
-
161
- **调用 `save_expense` 前必须执行:**
162
-
163
- #### 第 1 步:调用 `get_text_parse_prompt` 获取解析 Prompt
164
-
165
- **文字记账(如"午餐 24块")必须先调用此工具!**
166
-
167
- ```
168
- 1. 调用 get_text_parse_prompt()
169
- 2. 获取返回的 prompt、当前日期、当前时间
170
- 3. 将 prompt 作为 system message,用户输入作为 user message
171
- 4. 调用大模型解析,获取 JSON 结果
172
- ```
173
-
174
- **解析结果示例:**
175
- ```json
176
- {
177
- "name": "午餐",
178
- "amount": 24,
179
- "category": "餐饮",
180
- "datetime": "2026-04-14T12:30:00+08:00",
181
- "transactionType": "expense"
182
- }
183
- ```
184
-
185
- #### 第 2 步:调用 `save_expense` 保存
186
-
187
- | 字段 | 是否必须 | 说明 |
188
- |------|----------|------|
189
- | `name` | ✅ | 商品/服务名称(从解析结果获取)|
190
- | `amount` | ✅ | 金额(正数,从解析结果获取)|
191
- | `category` | ✅ **新增** | 分类(从解析结果获取)|
192
- | `date` | ✅ | 日期时间(从解析结果的 `datetime` 获取)|
193
- | `store` | 可选 | 商家名称 |
194
- | `note` | 可选 | 备注 |
195
-
196
- **⚠️ 重要**:
197
- - **文字记账必须先调用 `get_text_parse_prompt` 解析!**
198
- - **date 必须带时区**(如:`2026-04-14T12:30:00+08:00`)
199
- - 解析 Prompt 会自动处理时间推断("午餐"→12:30,"晚餐"→19:00)
100
+ **必填字段:** `name`、`amount`、`category`、`date`
200
101
 
201
102
  ---
202
103
 
203
- ### 🚨 小票字段说明(save_receipt)
204
-
205
- **从 prompt 解析结果中提取的所有字段,必须全部传递给 `save_receipt`**:
206
-
207
- | 字段 | 是否必须 | 常见遗漏 | 说明 |
208
- |------|----------|----------|------|
209
- | `store` | ✅ | 否 | 商家名称 |
210
- | `date` | ✅ **高频遗漏** | **是** | ISO 8601 格式,**必须带时区**,如:`2026-01-08T11:42:27+08:00` |
211
- | `totalAmount` | ✅ | 否 | 商品原价总和 |
212
- | `actualAmount` | ✅ **高频遗漏** | **是** | 实付金额(优惠后)|
213
- | `originalAmount` | ✅ **高频遗漏** | **是** | 原价(优惠前)|
214
- | `discountAmount` | ✅ **高频遗漏** | **是** | 优惠金额 |
215
- | `paymentMethod` | 可选 | 是 | 如:支付宝支付、微信支付 |
216
- | `items` | ✅ | 否 | 商品数组,每个必须有完整字段 |
217
-
218
- **items 数组中每个商品必须包含:**
219
-
220
- | 字段 | 是否必须 | 说明 |
221
- |------|----------|------|
222
- | `name` | ✅ | 商品名称 |
223
- | `amount` | ✅ | 金额(= price × quantity)|
224
- | `price` | ✅ | 单价 |
225
- | `quantity` | ✅ | 数量 |
226
- | `category` | ✅ **新增** | 分类(如:食品、日用品)|
227
-
228
- **⚠️ 重要**:
229
- - 缺少必填字段会导致保存失败。服务器会自动校验数据完整性和准确性。
230
- - **date 必须带时区**(如 `+08:00` 表示北京时间)
231
- - **如果不确定具体时间,不要传 date 参数**,系统会自动使用当前时间
232
- - **禁止传默认时间**(如 `08:00:00`),这会导致时间记录错误
104
+ ### 小票记账(save_receipt)
105
+
106
+ **流程:**
107
+ 1. 调用 `get_parse_prompt` 获取 prompt 模板
108
+ 2. 使用 prompt 调用大模型解析小票图片
109
+ 3. **字段核对**:确保所有解析结果字段都传给 `save_receipt`
110
+ 4. 调用 `save_receipt` 保存
111
+
112
+ **必填字段:**
113
+ - `store`:商家名称
114
+ - `date`:ISO 8601 格式,必须带时区
115
+ - `totalAmount`:商品原价总和
116
+ - `actualAmount`:实付金额(优惠后)
117
+ - `originalAmount`:原价(优惠前)
118
+ - `discountAmount`:优惠金额
119
+ - `items`:商品数组,每个商品必须有 `name`、`amount`、`price`、`quantity`、`category`
233
120
 
234
121
  ---
235
122
 
@@ -237,58 +124,24 @@ Agent 通过 MCP 协议自动获取工具列表,无需手动查询。
237
124
 
238
125
  | 风险等级 | 场景 | 策略 |
239
126
  |----------|------|------|
240
- | **高(必须确认)** | 金额 > 1000元、批量 > 10笔、退款/负数 | 列出详情,等待用户确认 |
241
- | **中(信息模糊时确认)** | 分类不明确、商家名称模糊 | 确认后执行 |
242
- | **低(直接执行)** | 信息完整明确 | 直接执行 |
127
+ | **高** | 金额 > 1000元、批量 > 10笔、退款/负数 | 必须确认 |
128
+ | **中** | 分类不明确、商家名称模糊 | 确认后执行 |
129
+ | **低** | 信息完整明确 | 直接执行 |
243
130
 
244
131
  ---
245
132
 
246
133
  ## 防重复录入(Agent 本地实现)
247
134
 
248
- **目标**:提醒用户可能的重复录入,但不阻止保存
249
-
250
- ### 实现方式
251
-
252
- Agent 维护本地记账记录日志(内存或本地文件):
253
-
254
- ```javascript
255
- // 记录格式
256
- {
257
- store: "商家名称",
258
- date: "2026-01-08", // 只记录到日期
259
- actualAmount: 25.88,
260
- timestamp: 1704694947000 // 记账时间戳
261
- }
262
- ```
263
-
264
- ### 重复检测规则
265
-
135
+ **规则:**
266
136
  - **时间窗口**:24 小时内
267
137
  - **匹配条件**:`store` + `date(到天)` + `actualAmount` 相同
268
- - **处理方式**:
269
- 1. 发现重复时,提醒用户:"⚠️ 24 小时内已有相似记录(华润万家春风店 25.88元),确认要重复录入吗?"
270
- 2. 用户确认后,继续调用 `save_receipt`
271
- 3. 用户取消,停止操作
272
-
273
- ### 代码示例
274
-
275
- ```javascript
276
- // 检查是否重复
277
- function isDuplicate(store, date, actualAmount) {
278
- const fingerprint = `${store}_${date.substring(0,10)}_${actualAmount}`;
279
- const recentRecord = recentLogs.find(log => {
280
- const timeDiff = Date.now() - log.timestamp;
281
- return log.fingerprint === fingerprint && timeDiff < 24 * 60 * 60 * 1000;
282
- });
283
- return recentRecord;
284
- }
285
- ```
138
+ - **处理方式**:提醒用户,用户确认后继续
286
139
 
287
140
  ---
288
141
 
289
142
  ## 回复规范(必须严格遵守)
290
143
 
291
- 记账成功后,回复内容必须包含以下格式:
144
+ 记账成功后,回复格式:
292
145
 
293
146
  ```
294
147
  ✅ 【商品名/店名】¥【金额】 已录入柴米AI记账。
@@ -296,65 +149,15 @@ function isDuplicate(store, date, actualAmount) {
296
149
  [自定义其他内容]
297
150
 
298
151
  MCP Server: v【版本号】
299
- 【友好正能量结束语】
152
+ 【友好结束语】
300
153
  ```
301
154
 
302
- ### 友好结束语参考
303
-
304
- | 分类 | 结束语 |
305
- |------|--------|
306
- | 餐饮类 | 用餐愉快!🍚 / 下午茶快乐!🧋 / 好好吃饭哦~ |
307
- | 交通类 | 出行顺利!🚗 |
308
- | 购物类 | 买得开心!🛍️ |
309
- | 收入类 | 入账顺利!💰 |
310
- | 通用 | 记账完成!继续保持~ / 理财小能手!✨ |
311
-
312
- ### 示例
313
-
314
- **用户:** 记一笔午餐 35元
315
-
316
- **Agent 回复:**
317
- ```
318
- ✅ 午餐¥35 已录入柴米AI记账。
319
-
320
- 今天也要好好吃饭哦~
321
-
322
- MCP Server: v3.1.23
323
- ```
324
-
325
- ---
326
-
327
- ## 使用示例
328
-
329
- ### 示例1:简单记账
330
-
331
- **用户:** 记一笔午餐 35元
332
-
333
- **Agent:**
334
- 1. 触发 Skill
335
- 2. 前置检查通过
336
- 3. 调用 `save_expense(name="午餐", amount=35, category="餐饮")`
337
- 4. 返回结果:✅ 已记录:午餐 35元
338
-
339
- ### 示例2:小票记账
340
-
341
- **用户:** 保存小票,沃尔玛,买了苹果5元、香蕉3元
342
-
343
- **Agent:**
344
- 1. 触发 Skill
345
- 2. 前置检查通过
346
- 3. 调用 `save_receipt(store="沃尔玛", items=[...], totalAmount=8)`
347
- 4. 返回结果:✅ 已保存小票:沃尔玛,共8元
348
-
349
- ### 示例3:首次使用(未授权)
350
-
351
- **用户:** 记一笔咖啡 25元
352
-
353
- **Agent:**
354
- 1. 触发 Skill
355
- 2. 前置检查发现未授权
356
- 3. 引导用户完成 Device Flow 授权
357
- 4. 授权完成后执行记账
155
+ **友好结束语参考:**
156
+ - 餐饮类:用餐愉快!🍚 / 好好吃饭哦~
157
+ - 交通类:出行顺利!🚗
158
+ - 购物类:买得开心!🛍️
159
+ - 收入类:入账顺利!💰
160
+ - 通用:记账完成!继续保持~
358
161
 
359
162
  ---
360
163
 
@@ -365,7 +168,6 @@ MCP Server: v3.1.23
365
168
  | 未安装 | 引导安装:`npm install -g chaimi-keep-mcp` |
366
169
  | 未授权 | 引导完成 Device Flow 授权 |
367
170
  | Token 过期 | 引导重新授权 |
368
- | 网络错误 | 提示用户检查网络,稍后重试 |
369
171
  | 参数错误 | 提示用户补充必要信息 |
370
172
 
371
173
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.1.33-beta.7",
3
+ "version": "3.1.34",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -139,7 +139,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
139
139
  properties: {
140
140
  name: { type: 'string', description: '商品名称(必填)' },
141
141
  amount: { type: 'number', description: '金额(必填)' },
142
- category: { type: 'string', description: '分类(可选,如:餐饮、食品、交通)' },
142
+ category: { type: 'string', description: '分类(必填,如:餐饮、食品、交通)' },
143
143
  store: { type: 'string', description: '商家名称(可选)' },
144
144
  date: { type: 'string', description: '消费时间(ISO格式)(可选)' },
145
145
  note: { type: 'string', description: '备注(可选)' },
@@ -158,11 +158,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
158
158
  type: 'object',
159
159
  properties: {
160
160
  store: { type: 'string', description: '商家名称(必填)' },
161
- date: { type: 'string', description: '消费时间(ISO 8601 格式,必须包含日期和时间,如:2026-04-10T13:06:21)' },
162
- totalAmount: { type: 'number', description: '总金额(所有商品amount之和)' },
163
- originalAmount: { type: 'number', description: '应付金额(优惠前)' },
164
- discountAmount: { type: 'number', description: '优惠金额' },
165
- actualAmount: { type: 'number', description: '实付金额(优惠后)' },
161
+ date: { type: 'string', description: '(必填)消费时间(ISO 8601 格式,必须包含日期和时间,如:2026-04-10T13:06:21)' },
162
+ totalAmount: { type: 'number', description: '(必填)总金额(所有商品amount之和)' },
163
+ originalAmount: { type: 'number', description: '(必填)应付金额(优惠前)' },
164
+ discountAmount: { type: 'number', description: '(必填)优惠金额' },
165
+ actualAmount: { type: 'number', description: '(必填)实付金额(优惠后)' },
166
166
  paymentMethod: { type: 'string', description: '支付方式,如:微信支付、支付宝' },
167
167
  receiptNo: { type: 'string', description: '小票编号' },
168
168
  items: {
@@ -179,7 +179,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
179
179
  amount: { type: 'number', description: '【必填】金额,必须等于 price × quantity' },
180
180
  weight: { type: 'string', description: '重量' },
181
181
  marketPrice: { type: 'string', description: '市场单价(元/500g)' },
182
- category: { type: 'string', description: '分类,如:餐饮、食品、购物' },
182
+ category: { type: 'string', description: '(必填)分类,如:餐饮、食品、购物' },
183
183
  },
184
184
  required: ['name', 'amount', 'price', 'quantity'],
185
185
  },
@@ -233,7 +233,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
233
233
  properties: {
234
234
  name: { type: 'string', description: '收入来源(如:工资、奖金、红包)' },
235
235
  amount: { type: 'number', description: '收入金额' },
236
- category: { type: 'string', description: '收入分类(如:工资、奖金、投资)' },
236
+ category: { type: 'string', description: '(必填)收入分类(如:工资、奖金、投资)' },
237
237
  store: { type: 'string', description: '付款方(如:公司名称)' },
238
238
  date: { type: 'string', description: '收入时间(ISO格式)(可选)' },
239
239
  note: { type: 'string', description: '备注' },
@@ -1090,19 +1090,13 @@ function validateQuantity(quantity) {
1090
1090
 
1091
1091
  /**
1092
1092
  * 格式化日期为 ISO 8601 格式(带时区)
1093
- * 解决时区转换问题:确保传入的本地时间正确存储
1093
+ * 注意:此函数只负责格式标准化,不负责语义解析
1094
+ * 语义解析(如"午餐"→12:00)应由 Agent 完成
1094
1095
  */
1095
1096
  function formatDateWithTimezone(dateStr) {
1096
- // 如果未提供日期,使用当前时间(北京时间)
1097
+ // 如果未提供日期,返回空字符串(让云函数使用服务器时间)
1097
1098
  if (!dateStr) {
1098
- const now = new Date();
1099
- const year = now.getFullYear();
1100
- const month = String(now.getMonth() + 1).padStart(2, '0');
1101
- const day = String(now.getDate()).padStart(2, '0');
1102
- const hours = String(now.getHours()).padStart(2, '0');
1103
- const minutes = String(now.getMinutes()).padStart(2, '0');
1104
- const seconds = String(now.getSeconds()).padStart(2, '0');
1105
- return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}+08:00`;
1099
+ return '';
1106
1100
  }
1107
1101
 
1108
1102
  try {
@@ -1117,16 +1111,11 @@ function formatDateWithTimezone(dateStr) {
1117
1111
  return dateStr + '+08:00';
1118
1112
  }
1119
1113
 
1120
- // 如果是纯日期格式(如 2026-04-14),使用当前时间
1114
+ // 如果是纯日期格式(如 2026-04-14),默认使用中午12:00
1115
+ // 注意:这是合理的默认值,因为纯日期通常表示"当天的消费"
1116
+ // 如果 Agent 需要更精确的时间(如晚餐18:00),应该在传入时就包含时间部分
1121
1117
  if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) {
1122
- const now = new Date();
1123
- const year = dateStr.slice(0, 4);
1124
- const month = dateStr.slice(5, 7);
1125
- const day = dateStr.slice(8, 10);
1126
- const hours = String(now.getHours()).padStart(2, '0');
1127
- const minutes = String(now.getMinutes()).padStart(2, '0');
1128
- const seconds = String(now.getSeconds()).padStart(2, '0');
1129
- return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}+08:00`;
1118
+ return `${dateStr}T12:00:00+08:00`;
1130
1119
  }
1131
1120
 
1132
1121
  // 其他情况,尝试解析并格式化为带时区的格式