feat(tender): 阶段3b 企业登录/注册 + 购买→支付→确认 闭环
- useHjcAuth:token 存 cookie(hjc_token),login/register/isLoggedIn
- 页面 /login /register(企业名称+密码)
- server/api/tender/{auth/login,auth/register,pay,mark-paid} 代理;order 代理从 cookie 读取 hjc_token 透传 Authorization
- BuyDocument:未登录跳登录;下单→pay(codeUrl)→确认 mark-paid→成功
- types 增 HjcUser
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
import type { HjcUser } from '~/types/tender'
|
||||||
|
|
||||||
|
/** 登录态(token 存 cookie,SSR 安全;服务器端代理从 cookie 读取并转发 Authorization) */
|
||||||
|
export function useHjcAuth() {
|
||||||
|
const token = useCookie<string | null>('hjc_token', {
|
||||||
|
maxAge: 60 * 60 * 24 * 7,
|
||||||
|
sameSite: 'lax'
|
||||||
|
})
|
||||||
|
const user = useState<HjcUser | null>('hjc-user', () => null)
|
||||||
|
|
||||||
|
function isLoggedIn() {
|
||||||
|
return !!token.value
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(enterpriseName: string, password: string) {
|
||||||
|
const res: any = await $fetch('/api/tender/login', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { enterpriseName, password }
|
||||||
|
})
|
||||||
|
if (res?.access_token) {
|
||||||
|
token.value = res.access_token
|
||||||
|
user.value = res.user || null
|
||||||
|
return { ok: true, user: res.user }
|
||||||
|
}
|
||||||
|
return { ok: false, message: res?.message || '登录失败' }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function register(payload: Record<string, any>) {
|
||||||
|
const res: any = await $fetch('/api/tender/register', {
|
||||||
|
method: 'POST',
|
||||||
|
body: payload
|
||||||
|
})
|
||||||
|
if (res?.access_token) {
|
||||||
|
token.value = res.access_token
|
||||||
|
user.value = res.user || null
|
||||||
|
return { ok: true, user: res.user }
|
||||||
|
}
|
||||||
|
return { ok: false, message: res?.message || '注册失败' }
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
token.value = null
|
||||||
|
user.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return { token, user, isLoggedIn, login, register, logout }
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-[60vh] flex items-center justify-center px-4">
|
||||||
|
<div class="w-full max-w-md rounded-lg border border-gray-200 p-8">
|
||||||
|
<h1 class="text-2xl font-bold text-center text-gray-900">企业登录</h1>
|
||||||
|
<form class="mt-6 space-y-4" @submit.prevent="submitForm">
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">企业名称</label>
|
||||||
|
<input v-model="enterpriseName" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="请输入企业名称" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">密码</label>
|
||||||
|
<input v-model="password" type="password" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="请输入密码" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="w-full rounded-md bg-blue-600 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50" :disabled="loading">
|
||||||
|
{{ loading ? '登录中...' : '登录' }}
|
||||||
|
</button>
|
||||||
|
<p v-if="error" class="text-center text-sm text-red-500">{{ error }}</p>
|
||||||
|
</form>
|
||||||
|
<p class="mt-4 text-center text-sm text-gray-500">
|
||||||
|
还没有账号?
|
||||||
|
<NuxtLink to="/register" class="text-blue-600 hover:underline">立即注册企业</NuxtLink>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { login } = useHjcAuth()
|
||||||
|
const route = useRoute()
|
||||||
|
const enterpriseName = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
|
||||||
|
async function submitForm() {
|
||||||
|
error.value = ''
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await login(enterpriseName.value, password.value)
|
||||||
|
if (res.ok) {
|
||||||
|
return navigateTo((route.query.redirect as string) || '/tender')
|
||||||
|
}
|
||||||
|
error.value = res.message || '登录失败'
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e?.message || '登录失败'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-[60vh] flex items-center justify-center px-4">
|
||||||
|
<div class="w-full max-w-md rounded-lg border border-gray-200 p-8">
|
||||||
|
<h1 class="text-2xl font-bold text-center text-gray-900">企业注册</h1>
|
||||||
|
<form class="mt-6 space-y-4" @submit.prevent="submitForm">
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">企业名称</label>
|
||||||
|
<input v-model="form.enterpriseName" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="企业名称" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">登录密码</label>
|
||||||
|
<input v-model="form.password" type="password" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="密码" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">纳税人识别号</label>
|
||||||
|
<input v-model="form.creditCode" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="统一社会信用代码" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">企业联系电话</label>
|
||||||
|
<input v-model="form.contactPhone" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="联系电话" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">企业邮箱</label>
|
||||||
|
<input v-model="form.contactEmail" type="email" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="邮箱" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-sm text-gray-600">经办人手机号</label>
|
||||||
|
<input v-model="form.agentPhone" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" placeholder="经办人手机号" />
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="w-full rounded-md bg-blue-600 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50" :disabled="loading">
|
||||||
|
{{ loading ? '注册中...' : '注册并登录' }}
|
||||||
|
</button>
|
||||||
|
<p v-if="error" class="text-center text-sm text-red-500">{{ error }}</p>
|
||||||
|
</form>
|
||||||
|
<p class="mt-4 text-center text-sm text-gray-500">
|
||||||
|
已注册?
|
||||||
|
<NuxtLink to="/login" class="text-blue-600 hover:underline">去登录</NuxtLink>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { register } = useHjcAuth()
|
||||||
|
const route = useRoute()
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const form = reactive({
|
||||||
|
enterpriseName: '',
|
||||||
|
password: '',
|
||||||
|
creditCode: '',
|
||||||
|
contactPhone: '',
|
||||||
|
contactEmail: '',
|
||||||
|
agentPhone: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
async function submitForm() {
|
||||||
|
error.value = ''
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await register({ ...form })
|
||||||
|
if (res.ok) {
|
||||||
|
return navigateTo((route.query.redirect as string) || '/tender')
|
||||||
|
}
|
||||||
|
error.value = res.message || '注册失败'
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e?.message || '注册失败'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -2,7 +2,17 @@
|
|||||||
<div class="container mx-auto px-4 py-10">
|
<div class="container mx-auto px-4 py-10">
|
||||||
<NuxtLink to="/tender" class="text-sm text-blue-600 hover:underline">← 返回标书中心</NuxtLink>
|
<NuxtLink to="/tender" class="text-sm text-blue-600 hover:underline">← 返回标书中心</NuxtLink>
|
||||||
|
|
||||||
<div v-if="tender" class="mx-auto mt-6 max-w-2xl rounded-lg border border-gray-200 p-6">
|
<!-- 未登录提示 -->
|
||||||
|
<div v-if="!loggedIn" class="mx-auto mt-6 max-w-xl rounded-lg border border-gray-200 p-8 text-center">
|
||||||
|
<p class="text-lg text-gray-700">购买标书需要先登录企业账号</p>
|
||||||
|
<div class="mt-6 flex gap-4 justify-center">
|
||||||
|
<NuxtLink :to="`/login?redirect=${redirect}`" class="rounded-md bg-blue-600 px-6 py-2 text-white hover:bg-blue-700">去登录</NuxtLink>
|
||||||
|
<NuxtLink :to="`/register?redirect=${redirect}`" class="rounded-md border border-gray-300 px-6 py-2 text-gray-700 hover:bg-gray-50">企业注册</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单 -->
|
||||||
|
<div v-else-if="tender && step === 'form'" class="mx-auto mt-6 max-w-2xl rounded-lg border border-gray-200 p-6">
|
||||||
<h1 class="text-xl font-bold text-gray-900">购买标书</h1>
|
<h1 class="text-xl font-bold text-gray-900">购买标书</h1>
|
||||||
<div class="mt-4 rounded-md bg-gray-50 p-4 text-sm">
|
<div class="mt-4 rounded-md bg-gray-50 p-4 text-sm">
|
||||||
<p class="font-semibold text-gray-800">{{ tender.projectName }}</p>
|
<p class="font-semibold text-gray-800">{{ tender.projectName }}</p>
|
||||||
@@ -30,19 +40,34 @@
|
|||||||
<input v-model.number="form.quantity" type="number" min="1" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" />
|
<input v-model.number="form.quantity" type="number" min="1" class="mt-1 w-full rounded-md border border-gray-300 px-4 py-2 text-sm" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button type="submit" class="w-full rounded-md bg-blue-600 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50" :disabled="submitting">
|
||||||
type="submit"
|
{{ submitting ? '提交中...' : '提交订单并用微信支付' }}
|
||||||
class="w-full rounded-md bg-blue-600 py-3 text-white font-medium hover:bg-blue-700 disabled:opacity-50"
|
|
||||||
:disabled="submitting"
|
|
||||||
>
|
|
||||||
{{ submitting ? '提交中...' : '提交订单并支付' }}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<p v-if="tip" class="text-center text-sm text-gray-500">{{ tip }}</p>
|
|
||||||
<p v-if="error" class="text-center text-sm text-red-500">{{ error }}</p>
|
<p v-if="error" class="text-center text-sm text-red-500">{{ error }}</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付 -->
|
||||||
|
<div v-else-if="step === 'pay'" class="mx-auto mt-6 max-w-2xl rounded-lg border border-gray-200 p-8 text-center">
|
||||||
|
<h1 class="text-xl font-bold text-gray-900">请使用微信扫码支付</h1>
|
||||||
|
<p class="mt-2 text-sm text-gray-500">订单号:{{ orderNo }}</p>
|
||||||
|
<p class="my-6 text-2xl font-bold text-blue-600">¥{{ formatMoney(totalAmount) }}</p>
|
||||||
|
<div class="rounded-md bg-gray-50 p-4 text-sm break-all text-gray-700">
|
||||||
|
扫码链接:<a :href="codeUrl" target="_blank" class="text-blue-600 hover:underline">{{ codeUrl }}</a>
|
||||||
|
</div>
|
||||||
|
<button class="mt-6 w-full rounded-md bg-blue-600 py-3 text-white font-medium hover:bg-blue-700" :disabled="marking" @click="markPaid">
|
||||||
|
{{ marking ? '确认中...' : '支付已完成,确认订单' }}
|
||||||
|
</button>
|
||||||
|
<p v-if="error" class="mt-3 text-sm text-red-500">{{ error }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 完成 -->
|
||||||
|
<div v-else-if="step === 'done'" class="mx-auto mt-6 max-w-2xl rounded-lg border border-green-200 bg-green-50 p-8 text-center">
|
||||||
|
<p class="text-xl font-bold text-green-700">购买成功</p>
|
||||||
|
<p class="mt-2 text-sm text-green-600">订单号:{{ orderNo }},标书已可查看/下载</p>
|
||||||
|
<NuxtLink to="/tender" class="mt-6 inline-block rounded-md bg-green-600 px-6 py-2 text-white hover:bg-green-700">返回标书中心</NuxtLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else-if="notFound" class="py-20 text-center text-gray-500">项目不存在</div>
|
<div v-else-if="notFound" class="py-20 text-center text-gray-500">项目不存在</div>
|
||||||
<SiteLoading v-else />
|
<SiteLoading v-else />
|
||||||
</div>
|
</div>
|
||||||
@@ -53,18 +78,21 @@ import type { Tender } from '~/types/tender'
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = route.query.id as string
|
const id = route.query.id as string
|
||||||
|
const { isLoggedIn } = useHjcAuth()
|
||||||
|
|
||||||
|
const loggedIn = ref(false)
|
||||||
const tender = shallowRef<Tender | null>(null)
|
const tender = shallowRef<Tender | null>(null)
|
||||||
const notFound = ref(false)
|
const notFound = ref(false)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const tip = ref('')
|
const marking = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
const step = ref<'form' | 'pay' | 'done'>('form')
|
||||||
|
const orderNo = ref('')
|
||||||
|
const codeUrl = ref('')
|
||||||
|
const totalAmount = ref(0)
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({ contactName: '', contactPhone: '', contactEmail: '', quantity: 1 })
|
||||||
contactName: '',
|
const redirect = computed(() => `/buy?id=${id}`)
|
||||||
contactPhone: '',
|
|
||||||
contactEmail: '',
|
|
||||||
quantity: 1
|
|
||||||
})
|
|
||||||
|
|
||||||
function formatMoney(n?: number | string) {
|
function formatMoney(n?: number | string) {
|
||||||
return Number(n ?? 0).toFixed(2)
|
return Number(n ?? 0).toFixed(2)
|
||||||
@@ -72,10 +100,9 @@ function formatMoney(n?: number | string) {
|
|||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
error.value = ''
|
error.value = ''
|
||||||
tip.value = ''
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
const res: any = await $fetch('/api/tender/order', {
|
const order: any = await $fetch('/api/tender/order', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
projectId: Number(id),
|
projectId: Number(id),
|
||||||
@@ -85,11 +112,15 @@ async function submit() {
|
|||||||
contactEmail: form.contactEmail
|
contactEmail: form.contactEmail
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (res && res.orderNo) {
|
if (order && order.orderNo) {
|
||||||
tip.value = `下单成功,订单号:${res.orderNo},请扫码支付`
|
orderNo.value = order.orderNo
|
||||||
// TODO(阶段3b): 调 /api/tender/pay 获取微信二维码(需登录态)
|
totalAmount.value = order.totalAmount || 0
|
||||||
|
// 发起支付
|
||||||
|
const pay: any = await $fetch('/api/tender/pay', { method: 'POST', body: { orderNo: order.orderNo } })
|
||||||
|
codeUrl.value = pay?.codeUrl || ''
|
||||||
|
step.value = 'pay'
|
||||||
} else {
|
} else {
|
||||||
error.value = res?.message || '下单失败'
|
error.value = order?.message || '下单失败'
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
error.value = e?.data?.message || e?.message || '下单失败'
|
error.value = e?.data?.message || e?.message || '下单失败'
|
||||||
@@ -98,6 +129,21 @@ async function submit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function markPaid() {
|
||||||
|
error.value = ''
|
||||||
|
marking.value = true
|
||||||
|
try {
|
||||||
|
await $fetch('/api/tender/mark-paid', { method: 'PUT', body: { orderNo: orderNo.value } })
|
||||||
|
step.value = 'done'
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e?.data?.message || e?.message || '确认失败'
|
||||||
|
} finally {
|
||||||
|
marking.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loggedIn.value = isLoggedIn()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
notFound.value = true
|
notFound.value = true
|
||||||
|
|||||||
@@ -48,3 +48,13 @@ export interface TenderPage {
|
|||||||
list: Tender[]
|
list: Tender[]
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 登录用户(对应后端 common.system.User 关键字段) */
|
||||||
|
export interface HjcUser {
|
||||||
|
userId: number
|
||||||
|
username: string
|
||||||
|
nickname?: string
|
||||||
|
phone?: string
|
||||||
|
tenantId?: number
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { $fetch } from 'ofetch'
|
||||||
|
import { createError, defineEventHandler, readBody } from 'h3'
|
||||||
|
import { useRuntimeConfig } from '#imports'
|
||||||
|
import { getTenantFromContext } from '../../../utils/tenant'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业登录
|
||||||
|
* POST /api/tender/login { enterpriseName, password }
|
||||||
|
* 代理到 mp-api /api/hjc/auth/login;成功返回 LoginResult{ access_token, user }。
|
||||||
|
*/
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const ctx = getTenantFromContext(event, config)
|
||||||
|
const body = await readBody(event)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await $fetch('/hjc/auth/login', {
|
||||||
|
baseURL: config.public.modulesApiBase,
|
||||||
|
method: 'POST',
|
||||||
|
headers: { TenantId: ctx.tenantId },
|
||||||
|
query: { TenantId: ctx.tenantId },
|
||||||
|
body
|
||||||
|
}) as any
|
||||||
|
const data = res && res.data !== undefined ? res.data : res
|
||||||
|
return data
|
||||||
|
} catch (error: any) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: error?.statusCode || error?.response?.status || 502,
|
||||||
|
statusMessage: error?.statusMessage || 'Failed to login'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { $fetch } from 'ofetch'
|
||||||
|
import { createError, defineEventHandler, readBody } from 'h3'
|
||||||
|
import { useRuntimeConfig } from '#imports'
|
||||||
|
import { getTenantFromContext } from '../../../utils/tenant'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 企业注册
|
||||||
|
* POST /api/tender/register { enterpriseName, password, creditCode, ... }
|
||||||
|
* 代理到 mp-api /api/hjc/auth/register;成功返回 LoginResult{ access_token, user }。
|
||||||
|
*/
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const ctx = getTenantFromContext(event, config)
|
||||||
|
const body = await readBody(event)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await $fetch('/hjc/auth/register', {
|
||||||
|
baseURL: config.public.modulesApiBase,
|
||||||
|
method: 'POST',
|
||||||
|
headers: { TenantId: ctx.tenantId },
|
||||||
|
query: { TenantId: ctx.tenantId },
|
||||||
|
body
|
||||||
|
}) as any
|
||||||
|
const data = res && res.data !== undefined ? res.data : res
|
||||||
|
return data
|
||||||
|
} catch (error: any) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: error?.statusCode || error?.response?.status || 502,
|
||||||
|
statusMessage: error?.statusMessage || 'Failed to register'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { $fetch } from 'ofetch'
|
||||||
|
import { createError, defineEventHandler, readBody, getHeader, getCookie } from 'h3'
|
||||||
|
import { useRuntimeConfig } from '#imports'
|
||||||
|
import { getTenantFromContext } from '../../utils/tenant'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记订单已支付(幂等)并触发一站式推送
|
||||||
|
* PUT /api/tender/mark-paid { orderNo }
|
||||||
|
* 代理到 mp-api /api/hjc/order/mark-paid;需登录态。
|
||||||
|
*/
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const ctx = getTenantFromContext(event, config)
|
||||||
|
const body = await readBody(event)
|
||||||
|
const cookieToken = getCookie(event, 'hjc_token')
|
||||||
|
const auth = getHeader(event, 'authorization') || (cookieToken ? `Bearer ${cookieToken}` : null)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await $fetch('/hjc/order/mark-paid', {
|
||||||
|
baseURL: config.public.modulesApiBase,
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
TenantId: ctx.tenantId,
|
||||||
|
...(auth ? { Authorization: auth } : {})
|
||||||
|
},
|
||||||
|
query: { TenantId: ctx.tenantId },
|
||||||
|
body
|
||||||
|
}) as any
|
||||||
|
const data = res && res.data !== undefined ? res.data : res
|
||||||
|
return data
|
||||||
|
} catch (error: any) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: error?.statusCode || error?.response?.status || 502,
|
||||||
|
statusMessage: error?.statusMessage || 'Failed to mark paid'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
import { $fetch } from 'ofetch'
|
import { $fetch } from 'ofetch'
|
||||||
import { createError, defineEventHandler, readBody, getHeader } from 'h3'
|
import { createError, defineEventHandler, readBody, getHeader, getCookie } from 'h3'
|
||||||
import { useRuntimeConfig } from '#imports'
|
import { useRuntimeConfig } from '#imports'
|
||||||
import { getTenantFromContext } from '../../utils/tenant'
|
import { getTenantFromContext } from '../../utils/tenant'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建标书订单
|
* 创建标书订单
|
||||||
* POST /api/tender/order { projectId, quantity, contactName, contactPhone, contactEmail }
|
* POST /api/tender/order { projectId, quantity, contactName, contactPhone, contactEmail }
|
||||||
* 代理到 mp-api(mp-java) /api/hjc/order/create;需登录态(Authorization 透传)。
|
* 代理到 mp-api(mp-java) /api/hjc/order/create;需登录态(从 cookie hjc_token 或 Authorization 透传)。
|
||||||
*/
|
*/
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const ctx = getTenantFromContext(event, config)
|
const ctx = getTenantFromContext(event, config)
|
||||||
const body = await readBody(event)
|
const body = await readBody(event)
|
||||||
const auth = getHeader(event, 'authorization')
|
const cookieToken = getCookie(event, 'hjc_token')
|
||||||
|
const auth = getHeader(event, 'authorization') || (cookieToken ? `Bearer ${cookieToken}` : null)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await $fetch('/hjc/order/create', {
|
const res = await $fetch('/hjc/order/create', {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { $fetch } from 'ofetch'
|
||||||
|
import { createError, defineEventHandler, readBody, getHeader, getCookie } from 'h3'
|
||||||
|
import { useRuntimeConfig } from '#imports'
|
||||||
|
import { getTenantFromContext } from '../../utils/tenant'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发起支付(微信Native扫码,返回 codeUrl)
|
||||||
|
* POST /api/tender/pay { orderNo }
|
||||||
|
* 代理到 mp-api /api/hjc/order/pay;需登录态。
|
||||||
|
*/
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const ctx = getTenantFromContext(event, config)
|
||||||
|
const body = await readBody(event)
|
||||||
|
const cookieToken = getCookie(event, 'hjc_token')
|
||||||
|
const auth = getHeader(event, 'authorization') || (cookieToken ? `Bearer ${cookieToken}` : null)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await $fetch('/hjc/order/pay', {
|
||||||
|
baseURL: config.public.modulesApiBase,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
TenantId: ctx.tenantId,
|
||||||
|
...(auth ? { Authorization: auth } : {})
|
||||||
|
},
|
||||||
|
query: { TenantId: ctx.tenantId },
|
||||||
|
body
|
||||||
|
}) as any
|
||||||
|
const data = res && res.data !== undefined ? res.data : res
|
||||||
|
return data
|
||||||
|
} catch (error: any) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: error?.statusCode || error?.response?.status || 502,
|
||||||
|
statusMessage: error?.statusMessage || 'Failed to pay'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user