diff --git a/docs/first-version-development-requirements.md b/docs/first-version-development-requirements.md index 5927193..96b7761 100644 --- a/docs/first-version-development-requirements.md +++ b/docs/first-version-development-requirements.md @@ -318,6 +318,7 @@ 6. 最终失败、超时失败需要退费。 7. 三网通道成本只用于平台内部成本核算,不影响客户扣费金额。 8. 当前版本计费口径固定为提交 accepted 扣费、最终 failed receipt/timeout 退款。 +9. 所有面向用户展示的金额、余额、充值金额、套餐价格和单价统一以人民币元展示并固定保留三位小数;内部仍使用分或最小计费单位持久化,不以展示精度改变账务计算。 ## 5. 功能需求 diff --git a/docs/system-functional-test-cases.md b/docs/system-functional-test-cases.md index 3139b5c..e8f7f9c 100644 --- a/docs/system-functional-test-cases.md +++ b/docs/system-functional-test-cases.md @@ -2275,6 +2275,7 @@ - 生成 account_transaction,类型为 recharge,关联 recharge_order。 - 客户端余额、账单流水即时可见。 - 运营端充值记录、账单流水、系统日志三处可追溯。 + - 所有金额、余额、套餐价格和单价固定展示三位小数,例如 `¥1.000`。 ### TC-BILLING-007 人工充值金额和短信条数只填其一 diff --git a/docs/testing-progress.md b/docs/testing-progress.md index 62abfb0..d6750f2 100644 --- a/docs/testing-progress.md +++ b/docs/testing-progress.md @@ -1540,5 +1540,6 @@ git diff --check - 逐图复查下载的《短信平台第一版瑕疵》后,确认中文图片文件名乱码仍真实存在:生产 `FileObject.fileName` 中可见 UTF-8 被按 Latin-1 解释后的值。上传链路现先恢复 multipart 文件名编码;历史记录由前端展示层兼容解码,避免签名材料和企业认证页继续显示乱码。 - 通用输入框、文本框去除内部填充色;同时覆盖 Chromium 自动填充产生的蓝色内层背景。企业认证页此前额外写死的灰色输入背景已移除。 +- 2026-07-11 回归发现此前金额展示验收不充分:充值记录和多处金额页面仍混用整数、两位或四位小数。现统一金额展示为人民币元三位小数,并新增输入框聚焦底色与文本选中高亮;需求和 `TC-BILLING-006` 已同步。 - 已执行 FilesService 定向单测(3 项通过)、API build、前端 build 和 `git diff --check`;生产部署后四个服务均为 active,API/Gateway health 正常。通过真实 `POST /api/admin/files/upload` 上传 `营业执照-编码回归.png`,响应和 `FileObject` 持久化文件名均为正常中文。 - Batch 6 的 6.1、6.2、7.1、8.1 仍为待修,不得因之前的前端构建通过而标记完成;其余 8.x 与客户端菜单顺序将继续按原始文档逐项复核。 diff --git a/src/apps/admin/AdminChannelsPage.tsx b/src/apps/admin/AdminChannelsPage.tsx index 2156d69..66e8103 100644 --- a/src/apps/admin/AdminChannelsPage.tsx +++ b/src/apps/admin/AdminChannelsPage.tsx @@ -627,7 +627,7 @@ export function AdminChannelsPage() {
{carrierLabelMap[channel.carrier]} - {channel.unitPrice.toFixed(1)} 分 + {channel.unitPrice.toFixed(3)} 分
{statusLabelMap[channel.status]} diff --git a/src/apps/admin/AdminCustomerDetailPage.tsx b/src/apps/admin/AdminCustomerDetailPage.tsx index af06cf2..7c58239 100644 --- a/src/apps/admin/AdminCustomerDetailPage.tsx +++ b/src/apps/admin/AdminCustomerDetailPage.tsx @@ -3,6 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom'; import { ArrowLeft, FileText, MessageSquare, Server } from 'lucide-react'; import { adminApi, type ClientSmsSignature, type ClientSmsTemplate, type EnterpriseApplication, type TenantAccount, type TenantOption } from '@/api/adminApi'; import { Breadcrumb, Button, Table, Tag, type TableColumn } from '@/components/ui'; +import { formatCents } from '@/utils/currency'; export function AdminCustomerDetailPage() { const navigate = useNavigate(); @@ -67,7 +68,7 @@ export function AdminCustomerDetailPage() {
企业编码{tenant?.code ?? '-'}{tenant?.status ?? '-'}
短信余量{(account?.smsUnits ?? 0).toLocaleString('zh-CN')}真实账户余量
-
现金余额¥{((account?.balanceCents ?? 0) / 100).toLocaleString('zh-CN')}真实账户余额
+
现金余额¥{formatCents(account?.balanceCents)}真实账户余额
diff --git a/src/apps/admin/AdminCustomersPage.tsx b/src/apps/admin/AdminCustomersPage.tsx index 54349db..09bf94a 100644 --- a/src/apps/admin/AdminCustomersPage.tsx +++ b/src/apps/admin/AdminCustomersPage.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { Building2, DollarSign, Plus, Trash2, TrendingDown, TrendingUp } from 'lucide-react'; import { adminApi, type TenantManagementRow } from '@/api/adminApi'; import { Breadcrumb, Button, Input, Modal, Select, Table, Tag, Textarea, type TableColumn } from '@/components/ui'; +import { formatCents } from '@/utils/currency'; type AdminCustomersPageProps = { basePath?: string; @@ -16,10 +17,6 @@ type RechargeForm = { remark: string; }; -function formatCurrency(cents: number) { - return (cents / 100).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); -} - function ConfirmModal({ message, onCancel, onConfirm }: { message: string; onCancel: () => void; onConfirm: () => void }) { return ( } onClose={onCancel} open title="操作确认"> @@ -86,14 +83,14 @@ export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCusto const balance = record.account?.balanceCents ?? 0; return ( - ¥{formatCurrency(balance)} + ¥{formatCents(balance)} {balance < 0 ? 欠费 : null} ); }, }, - { key: 'overdraftLimit', title: '透支限额', width: '150px', align: 'right', render: (record) => `¥${formatCurrency(record.account?.creditCents ?? 0)}` }, - { key: 'todaySpend', title: '今日消费', width: '150px', align: 'right', render: (record) => `¥${formatCurrency(record.todaySpendCents)}` }, + { key: 'overdraftLimit', title: '透支限额', width: '150px', align: 'right', render: (record) => `¥${formatCents(record.account?.creditCents ?? 0)}` }, + { key: 'todaySpend', title: '今日消费', width: '150px', align: 'right', render: (record) => `¥${formatCents(record.todaySpendCents)}` }, { key: 'status', title: '企业状态', width: '130px', render: (record) => {record.status === 'active' ? '正常' : '已禁用'} }, { key: 'actions', @@ -172,7 +169,7 @@ export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCusto
企业总数{records.length}真实租户数量。
正常运营{activeCount}可正常提交发送任务。
已禁用{disabledCount}已暂停发送能力。
-
账户余额¥{formatCurrency(totalBalance)}企业账户余额汇总。
+
账户余额¥{formatCents(totalBalance)}企业账户余额汇总。
@@ -215,7 +212,7 @@ export function AdminCustomersPage({ basePath = '/admin/customers' }: AdminCusto >
- + updateRechargeForm('amount', event.target.value)} prefix="¥" required type="number" value={rechargeForm.amount} /> updateRechargeForm('operator', event.target.value)} value={rechargeForm.operator} />