整理代码
This commit is contained in:
@@ -46,9 +46,9 @@ import kotlin.uuid.Uuid
|
||||
|
||||
object BlueInvoiceDao {
|
||||
|
||||
fun listBatchNos(userId: Uuid): List<String> =
|
||||
fun listBatchNos(userId: Uuid, enterpriseId: Uuid?, digitalAccountId: Uuid?): List<String> =
|
||||
OpenInvoiceBatchTable.selectAll()
|
||||
.where { OpenInvoiceBatchTable.userId eq userId }
|
||||
.where { batchScopeWhere(userId, enterpriseId, digitalAccountId) }
|
||||
.orderBy(OpenInvoiceBatchTable.createdAt, SortOrder.DESC)
|
||||
.map { it[OpenInvoiceBatchTable.batchNo] }
|
||||
|
||||
@@ -69,14 +69,7 @@ object BlueInvoiceDao {
|
||||
isSuccess: Boolean? = null,
|
||||
batchNo: String? = null,
|
||||
): PageResult<InvoiceHistoryItem> {
|
||||
val conditions = mutableListOf<Op<Boolean>>()
|
||||
if (digitalAccountId != null) {
|
||||
conditions.add(HistoryInvoiceBasicTable.digitalAccountId eq digitalAccountId)
|
||||
} else if (enterpriseId != null) {
|
||||
conditions.add(HistoryInvoiceBasicTable.enterpriseId eq enterpriseId)
|
||||
} else {
|
||||
conditions.add(HistoryInvoiceBasicTable.userId eq userId)
|
||||
}
|
||||
val conditions = mutableListOf(invoiceScopeWhere(userId, enterpriseId, digitalAccountId))
|
||||
conditions.add(HistoryInvoiceBasicTable.deletedAt.isNull())
|
||||
|
||||
// 发票类型筛选:前端传 BLUE/RED,库中存 1/2
|
||||
@@ -105,7 +98,7 @@ object BlueInvoiceDao {
|
||||
val matchedSerialNos = (OpenInvoiceBatchItemTable innerJoin OpenInvoiceBatchTable)
|
||||
.selectAll()
|
||||
.where {
|
||||
(OpenInvoiceBatchTable.userId eq userId) and
|
||||
batchScopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
(OpenInvoiceBatchTable.batchNo like "%$normalizedBatchNo%")
|
||||
}
|
||||
.map { it[OpenInvoiceBatchItemTable.invoiceReqSerialNo] }
|
||||
@@ -138,7 +131,7 @@ object BlueInvoiceDao {
|
||||
(OpenInvoiceBatchItemTable innerJoin OpenInvoiceBatchTable)
|
||||
.selectAll()
|
||||
.where {
|
||||
(OpenInvoiceBatchTable.userId eq userId) and
|
||||
batchScopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
(OpenInvoiceBatchItemTable.invoiceReqSerialNo inList serialNos)
|
||||
}
|
||||
.associate { row ->
|
||||
@@ -451,18 +444,6 @@ object BlueInvoiceDao {
|
||||
it[HistoryInvoiceBasicTable.invDeletedFlag] = req.invDeletedFlag ?: "0"
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流水号查询记录的 userId
|
||||
*/
|
||||
fun findUserIdBySerialNo(invoiceReqSerialNo: String): Uuid {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where { HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo }
|
||||
.singleOrNull()
|
||||
?: throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "发票记录不存在")
|
||||
return row[HistoryInvoiceBasicTable.userId]
|
||||
?: throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "发票记录不存在用户信息")
|
||||
}
|
||||
|
||||
fun findInvoiceScopeBySerialNo(invoiceReqSerialNo: String): InvoiceScope {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where { HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo }
|
||||
@@ -528,10 +509,15 @@ object BlueInvoiceDao {
|
||||
.distinct()
|
||||
}
|
||||
|
||||
fun invoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? {
|
||||
fun invoiceDownloadUrl(
|
||||
userId: Uuid,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
invoiceReqSerialNo: String,
|
||||
): InvoiceDownloadUrlResponse? {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
(HistoryInvoiceBasicTable.userId eq userId) and
|
||||
invoiceScopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
(HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo) and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
}
|
||||
@@ -543,14 +529,35 @@ object BlueInvoiceDao {
|
||||
/**
|
||||
* 查询发票完整详情(含商品明细、差额征税凭证、关联单据)
|
||||
*/
|
||||
fun invoiceDetail(userId: Uuid, invoiceReqSerialNo: String): InvoiceDetailResponse? {
|
||||
fun invoiceDetail(
|
||||
userId: Uuid,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
invoiceReqSerialNo: String,
|
||||
): InvoiceDetailResponse? {
|
||||
val basicRow = HistoryInvoiceBasicTable.selectAll().where {
|
||||
(HistoryInvoiceBasicTable.userId eq userId) and (HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo)
|
||||
invoiceScopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
(HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo) and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
}.singleOrNull() ?: return null
|
||||
|
||||
return buildInvoiceDetailResponse(basicRow)
|
||||
}
|
||||
|
||||
private fun invoiceScopeWhere(userId: Uuid, enterpriseId: Uuid?, digitalAccountId: Uuid?): Op<Boolean> =
|
||||
when {
|
||||
digitalAccountId != null -> HistoryInvoiceBasicTable.digitalAccountId eq digitalAccountId
|
||||
enterpriseId != null -> HistoryInvoiceBasicTable.enterpriseId eq enterpriseId
|
||||
else -> HistoryInvoiceBasicTable.userId eq userId
|
||||
}
|
||||
|
||||
private fun batchScopeWhere(userId: Uuid, enterpriseId: Uuid?, digitalAccountId: Uuid?): Op<Boolean> =
|
||||
when {
|
||||
digitalAccountId != null -> OpenInvoiceBatchTable.digitalAccountId eq digitalAccountId
|
||||
enterpriseId != null -> OpenInvoiceBatchTable.enterpriseId eq enterpriseId
|
||||
else -> OpenInvoiceBatchTable.userId eq userId
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主表行构建发票完整详情响应
|
||||
*/
|
||||
|
||||
@@ -92,19 +92,19 @@ object EnterpriseManageDao {
|
||||
.where { (PtDigitalAccountTable.id eq id) and PtDigitalAccountTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
|
||||
fun digitalAccountByApiKey(apiKey: String): ResultRow? =
|
||||
fun digitalAccountByTaxpayerAndAccount(taxpayerNum: String, account: String): ResultRow? =
|
||||
PtDigitalAccountTable.selectAll()
|
||||
.where {
|
||||
(PtDigitalAccountTable.apiKey eq apiKey) and
|
||||
(PtDigitalAccountTable.status eq "ENABLED") and
|
||||
(PtDigitalAccountTable.taxpayerNum eq taxpayerNum) and
|
||||
(PtDigitalAccountTable.account eq account) and
|
||||
PtDigitalAccountTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
fun digitalAccountForUser(userId: Uuid): ResultRow? =
|
||||
fun digitalAccountByApiKey(apiKey: String): ResultRow? =
|
||||
PtDigitalAccountTable.selectAll()
|
||||
.where {
|
||||
(PtDigitalAccountTable.platformUserId eq userId) and
|
||||
(PtDigitalAccountTable.apiKey eq apiKey) and
|
||||
(PtDigitalAccountTable.status eq "ENABLED") and
|
||||
PtDigitalAccountTable.deletedAt.isNull()
|
||||
}
|
||||
@@ -183,7 +183,6 @@ object EnterpriseManageDao {
|
||||
it[PtDigitalAccountTable.name] = name
|
||||
it[PtDigitalAccountTable.identityType] = identityType
|
||||
it[PtDigitalAccountTable.platformUserId] = platformUserId
|
||||
it[PtDigitalAccountTable.apiKey] = apiKey
|
||||
it[updatedAt] = now
|
||||
}
|
||||
return id
|
||||
@@ -201,6 +200,13 @@ object EnterpriseManageDao {
|
||||
}[PtDigitalAccountTable.id]
|
||||
}
|
||||
|
||||
fun updateDigitalAccountStatus(digitalAccountId: Uuid, status: String) {
|
||||
PtDigitalAccountTable.update({ PtDigitalAccountTable.id eq digitalAccountId }) {
|
||||
it[PtDigitalAccountTable.status] = status
|
||||
it[updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun bindDigitalAccountUser(digitalAccountId: Uuid, userId: Uuid) {
|
||||
PtDigitalAccountTable.update({ PtDigitalAccountTable.id eq digitalAccountId }) {
|
||||
it[platformUserId] = userId
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package com.bbit.ticket.dao.piaotong
|
||||
|
||||
import com.bbit.ticket.database.piaotong.HistoryInvoiceBasicTable
|
||||
import org.jetbrains.exposed.v1.core.Op
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.isNull
|
||||
@@ -41,14 +42,14 @@ object HistoryDao {
|
||||
/**
|
||||
* 根据历史记录 ID 查询蓝票基本信息
|
||||
*/
|
||||
fun findByHistory(historyId: Uuid?, userId: Uuid): HistoryRow {
|
||||
fun findByHistory(historyId: Uuid?, userId: Uuid, enterpriseId: Uuid?, digitalAccountId: Uuid?): HistoryRow {
|
||||
if (historyId == null) {
|
||||
throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "历史记录 ID 为空")
|
||||
}
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
(HistoryInvoiceBasicTable.id eq historyId) and
|
||||
(HistoryInvoiceBasicTable.userId eq userId) and
|
||||
scopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
@@ -79,4 +80,11 @@ object HistoryDao {
|
||||
)
|
||||
}
|
||||
|
||||
private fun scopeWhere(userId: Uuid, enterpriseId: Uuid?, digitalAccountId: Uuid?): Op<Boolean> =
|
||||
when {
|
||||
digitalAccountId != null -> HistoryInvoiceBasicTable.digitalAccountId eq digitalAccountId
|
||||
enterpriseId != null -> HistoryInvoiceBasicTable.enterpriseId eq enterpriseId
|
||||
else -> HistoryInvoiceBasicTable.userId eq userId
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.bbit.ticket.database.piaotong.HistoryInvoiceRedTable
|
||||
import com.bbit.ticket.entity.request.QuickRedInvoiceRequest
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.RedInvoiceInfoResponse
|
||||
import org.jetbrains.exposed.v1.core.Op
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.isNull
|
||||
@@ -66,7 +67,7 @@ object RedInvoiceDao {
|
||||
it[HistoryInvoiceBasicTable.buyerBankAccount] = blueRow?.get(HistoryInvoiceBasicTable.buyerBankAccount)
|
||||
|
||||
// ---- 金额 ----
|
||||
val redAmount = req.amount?.toBigDecimalOrNull() ?: BigDecimal.ZERO
|
||||
val redAmount = req.amount.toBigDecimalOrNull() ?: BigDecimal.ZERO
|
||||
it[HistoryInvoiceBasicTable.noTaxAmount] = redAmount
|
||||
it[HistoryInvoiceBasicTable.taxAmount] = BigDecimal.ZERO
|
||||
it[HistoryInvoiceBasicTable.amountWithTax] = redAmount
|
||||
@@ -89,7 +90,7 @@ object RedInvoiceDao {
|
||||
it[HistoryInvoiceRedTable.historyId] = historyId
|
||||
it[HistoryInvoiceRedTable.invoiceReqSerialNo] = req.invoiceReqSerialNo
|
||||
it[HistoryInvoiceRedTable.redReason] = req.redReason
|
||||
it[HistoryInvoiceRedTable.amount] = req.amount?.toBigDecimalOrNull()
|
||||
it[HistoryInvoiceRedTable.amount] = req.amount.toBigDecimalOrNull()
|
||||
it[HistoryInvoiceRedTable.invoiceCode] = req.invoiceCode
|
||||
it[HistoryInvoiceRedTable.invoiceNo] = req.invoiceNo
|
||||
it[HistoryInvoiceRedTable.invoiceKind] = req.invoiceKind
|
||||
@@ -114,11 +115,24 @@ object RedInvoiceDao {
|
||||
/**
|
||||
* 根据流水号查询红票申请信息
|
||||
*/
|
||||
fun findRedInfoBySerialNo(userId: Uuid, invoiceReqSerialNo: String): RedInvoiceInfoResponse? {
|
||||
fun findRedInfoBySerialNo(
|
||||
userId: Uuid,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
invoiceReqSerialNo: String,
|
||||
): RedInvoiceInfoResponse? {
|
||||
val canAccess = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
scopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
(HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo) and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
}
|
||||
.any()
|
||||
if (!canAccess) return null
|
||||
|
||||
val row = HistoryInvoiceRedTable.selectAll()
|
||||
.where {
|
||||
(HistoryInvoiceRedTable.userId eq userId) and
|
||||
(HistoryInvoiceRedTable.invoiceReqSerialNo eq invoiceReqSerialNo)
|
||||
HistoryInvoiceRedTable.invoiceReqSerialNo eq invoiceReqSerialNo
|
||||
}
|
||||
.singleOrNull() ?: return null
|
||||
|
||||
@@ -130,10 +144,15 @@ object RedInvoiceDao {
|
||||
)
|
||||
}
|
||||
|
||||
fun invoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? {
|
||||
fun invoiceDownloadUrl(
|
||||
userId: Uuid,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
invoiceReqSerialNo: String,
|
||||
): InvoiceDownloadUrlResponse? {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
(HistoryInvoiceBasicTable.userId eq userId) and
|
||||
scopeWhere(userId, enterpriseId, digitalAccountId) and
|
||||
(HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo) and
|
||||
(HistoryInvoiceBasicTable.invoiceType eq "2") and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
@@ -142,4 +161,11 @@ object RedInvoiceDao {
|
||||
|
||||
return InvoiceDownloadUrlResponse(row[HistoryInvoiceBasicTable.downloadUrl])
|
||||
}
|
||||
|
||||
private fun scopeWhere(userId: Uuid, enterpriseId: Uuid?, digitalAccountId: Uuid?): Op<Boolean> =
|
||||
when {
|
||||
digitalAccountId != null -> HistoryInvoiceBasicTable.digitalAccountId eq digitalAccountId
|
||||
enterpriseId != null -> HistoryInvoiceBasicTable.enterpriseId eq enterpriseId
|
||||
else -> HistoryInvoiceBasicTable.userId eq userId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,3 +49,8 @@ data class UpdateInvoiceSettingRequest(
|
||||
val address: String = "",
|
||||
val phone: String = "",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class UpdateDigitalAccountStatusRequest(
|
||||
val status: String,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* 发票数据获取请求参数
|
||||
*
|
||||
* 用于查询企业开具/取得的发票信息
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceQueryRequest(
|
||||
|
||||
/**
|
||||
* 纳税人识别号
|
||||
*
|
||||
* 长度:15-20
|
||||
* 仅支持大写字母和数字
|
||||
*/
|
||||
@SerialName("taxpayerNum")
|
||||
val taxpayerNum: String,
|
||||
|
||||
/**
|
||||
* 查询类型
|
||||
*
|
||||
* 1:开具发票
|
||||
* 2:取得发票
|
||||
*/
|
||||
@SerialName("queryType")
|
||||
val queryType: String,
|
||||
|
||||
/**
|
||||
* 发票来源
|
||||
*
|
||||
* 0:全部
|
||||
* 1:增值税发票管理系统
|
||||
* 2:电子发票服务平台
|
||||
*/
|
||||
@SerialName("invSource")
|
||||
val invSource: String? = null,
|
||||
|
||||
/**
|
||||
* 发票种类代码
|
||||
*
|
||||
* 81:电子发票(增值税专用发票)
|
||||
* 82:电子发票(普通发票)
|
||||
* 10:增值税电子普通发票
|
||||
* 08:增值税电子专用发票
|
||||
* 04:增值税普通发票
|
||||
* 01:增值税专用发票
|
||||
*/
|
||||
@SerialName("invoiceKind")
|
||||
val invoiceKind: String? = null,
|
||||
|
||||
/**
|
||||
* 发票状态
|
||||
*
|
||||
* 空字符串:全部
|
||||
* 01:正常
|
||||
* 02:已作废
|
||||
* 03:已红冲-全额
|
||||
* 04:已红冲-部分
|
||||
*/
|
||||
@SerialName("invoiceState")
|
||||
val invoiceState: String? = null,
|
||||
|
||||
/**
|
||||
* 数电号码
|
||||
*
|
||||
* 查询数电发票时必填
|
||||
* 与 invoiceCode 至少传一个
|
||||
*/
|
||||
@SerialName("electronicInvoiceNo")
|
||||
val electronicInvoiceNo: String? = null,
|
||||
|
||||
/**
|
||||
* 发票代码
|
||||
*
|
||||
* 长度:10 或 12
|
||||
* 查询税控系统发票时必填
|
||||
*/
|
||||
@SerialName("invoiceCode")
|
||||
val invoiceCode: String? = null,
|
||||
|
||||
/**
|
||||
* 发票号码
|
||||
*
|
||||
* 长度:8
|
||||
* 查询税控系统发票时必填
|
||||
*/
|
||||
@SerialName("invoiceNo")
|
||||
val invoiceNo: String? = null,
|
||||
|
||||
/**
|
||||
* 开票日期
|
||||
*
|
||||
* 格式:yyyyMMdd
|
||||
* 示例:20221123
|
||||
*/
|
||||
@SerialName("invoiceDate")
|
||||
val invoiceDate: String,
|
||||
|
||||
/**
|
||||
* 对方纳税人税号
|
||||
*/
|
||||
@SerialName("reciprocalTaxpayerNum")
|
||||
val reciprocalTaxpayerNum: String? = null,
|
||||
|
||||
/**
|
||||
* 对方纳税人名称
|
||||
*
|
||||
* 长度:4-100
|
||||
*/
|
||||
@SerialName("reciprocalTaxpayerName")
|
||||
val reciprocalTaxpayerName: String? = null,
|
||||
|
||||
/**
|
||||
* 电子税局登录账号
|
||||
*/
|
||||
@SerialName("account")
|
||||
val account: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class InvoiceQuerySubmitRequest(
|
||||
@SerialName("digitalAccountId")
|
||||
val digitalAccountId: String? = null,
|
||||
|
||||
@SerialName("queryType")
|
||||
val queryType: String,
|
||||
|
||||
@SerialName("invSource")
|
||||
val invSource: String? = null,
|
||||
|
||||
@SerialName("invoiceKind")
|
||||
val invoiceKind: String? = null,
|
||||
|
||||
@SerialName("invoiceState")
|
||||
val invoiceState: String? = null,
|
||||
|
||||
@SerialName("electronicInvoiceNo")
|
||||
val electronicInvoiceNo: String? = null,
|
||||
|
||||
@SerialName("invoiceCode")
|
||||
val invoiceCode: String? = null,
|
||||
|
||||
@SerialName("invoiceDate")
|
||||
val invoiceDate: String,
|
||||
|
||||
@SerialName("reciprocalTaxpayerNum")
|
||||
val reciprocalTaxpayerNum: String? = null,
|
||||
|
||||
@SerialName("reciprocalTaxpayerName")
|
||||
val reciprocalTaxpayerName: String? = null,
|
||||
)
|
||||
@@ -0,0 +1,348 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* 发票数据获取响应报文
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceQueryResponse(
|
||||
|
||||
/**
|
||||
* 获取结果代码
|
||||
*
|
||||
* 0000:获取成功
|
||||
* 其他:获取失败,失败原因见 resultMsg
|
||||
*/
|
||||
@SerialName("resultCode")
|
||||
val resultCode: String,
|
||||
|
||||
/**
|
||||
* 获取结果描述
|
||||
*
|
||||
* 成功或失败时返回的说明信息
|
||||
*/
|
||||
@SerialName("resultMsg")
|
||||
val resultMsg: String? = null,
|
||||
|
||||
/**
|
||||
* 发票列表
|
||||
*
|
||||
* 兼容后续批量查询场景
|
||||
*/
|
||||
@SerialName("invoiceList")
|
||||
val invoiceList: List<InvoiceInfo> = emptyList()
|
||||
)
|
||||
|
||||
/**
|
||||
* 发票信息
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceInfo(
|
||||
|
||||
/**
|
||||
* 购买方名称
|
||||
*/
|
||||
@SerialName("buyerName")
|
||||
val buyerName: String,
|
||||
|
||||
/**
|
||||
* 购买方纳税人识别号
|
||||
*/
|
||||
@SerialName("buyerTaxpayerNum")
|
||||
val buyerTaxpayerNum: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方地址电话
|
||||
*/
|
||||
@SerialName("buyerAddress")
|
||||
val buyerAddress: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方开户行及账号
|
||||
*/
|
||||
@SerialName("buyerBankName")
|
||||
val buyerBankName: String? = null,
|
||||
|
||||
/**
|
||||
* 销售方纳税人识别号
|
||||
*/
|
||||
@SerialName("sellerTaxpayerNum")
|
||||
val sellerTaxpayerNum: String,
|
||||
|
||||
/**
|
||||
* 销售方名称
|
||||
*/
|
||||
@SerialName("sellerName")
|
||||
val sellerName: String,
|
||||
|
||||
/**
|
||||
* 销售方地址电话
|
||||
*/
|
||||
@SerialName("sellerAddress")
|
||||
val sellerAddress: String? = null,
|
||||
|
||||
/**
|
||||
* 销售方开户行及账号
|
||||
*/
|
||||
@SerialName("sellerBankName")
|
||||
val sellerBankName: String? = null,
|
||||
|
||||
/**
|
||||
* 发票种类代码
|
||||
*
|
||||
* 81:电子发票(增值税专用发票)
|
||||
* 82:电子发票(普通发票)
|
||||
* 10:增值税电子普通发票
|
||||
* 08:增值税电子专用发票
|
||||
* 04:增值税普通发票
|
||||
* 01:增值税专用发票
|
||||
*/
|
||||
@SerialName("invoiceKind")
|
||||
val invoiceKind: String,
|
||||
|
||||
/**
|
||||
* 开票日期
|
||||
*
|
||||
* 格式:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
@SerialName("invoiceDate")
|
||||
val invoiceDate: String,
|
||||
|
||||
/**
|
||||
* 发票代码
|
||||
*
|
||||
* 税控系统发票可能返回
|
||||
*/
|
||||
@SerialName("invoiceCode")
|
||||
val invoiceCode: String? = null,
|
||||
|
||||
/**
|
||||
* 发票号码
|
||||
*
|
||||
* 税控系统发票可能返回
|
||||
*/
|
||||
@SerialName("invoiceNo")
|
||||
val invoiceNo: String? = null,
|
||||
|
||||
/**
|
||||
* 数电发票号码
|
||||
*/
|
||||
@SerialName("electronicInvoiceNo")
|
||||
val electronicInvoiceNo: String? = null,
|
||||
|
||||
/**
|
||||
* 不含税金额
|
||||
*
|
||||
* 保留小数点后 2 位
|
||||
*/
|
||||
@SerialName("noTaxAmount")
|
||||
val noTaxAmount: String,
|
||||
|
||||
/**
|
||||
* 税额
|
||||
*
|
||||
* 保留小数点后 2 位
|
||||
*/
|
||||
@SerialName("taxAmount")
|
||||
val taxAmount: String,
|
||||
|
||||
/**
|
||||
* 价税合计
|
||||
*
|
||||
* 保留小数点后 2 位
|
||||
*/
|
||||
@SerialName("amountWithTax")
|
||||
val amountWithTax: String,
|
||||
|
||||
/**
|
||||
* 作废标志
|
||||
*
|
||||
* NOT_DESTROY:未作废
|
||||
* ALREADY_DESTROY:已作废
|
||||
* DESTROYING:作废中
|
||||
* DESTROY_FAIL:作废失败
|
||||
*/
|
||||
@SerialName("invalidFlag")
|
||||
val invalidFlag: String,
|
||||
|
||||
/**
|
||||
* 冲红标志
|
||||
*
|
||||
* NOT_RED:未冲红
|
||||
* ALREADY_RED:已冲红
|
||||
* REDING:冲红中
|
||||
* RED_FAIL:冲红失败
|
||||
* PART_RED:部分冲红
|
||||
*/
|
||||
@SerialName("redFlag")
|
||||
val redFlag: String,
|
||||
|
||||
/**
|
||||
* 开票人名称
|
||||
*/
|
||||
@SerialName("drawerName")
|
||||
val drawerName: String,
|
||||
|
||||
/**
|
||||
* 收款人
|
||||
*/
|
||||
@SerialName("casherName")
|
||||
val casherName: String? = null,
|
||||
|
||||
/**
|
||||
* 复核人
|
||||
*/
|
||||
@SerialName("reviewerName")
|
||||
val reviewerName: String? = null,
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*
|
||||
* 数电发票一般限制 200 字符
|
||||
* 税控发票按 GBK 字节长度限制 240 字节
|
||||
*/
|
||||
@SerialName("remark")
|
||||
val remark: String? = null,
|
||||
|
||||
/**
|
||||
* 项目明细列表
|
||||
*/
|
||||
@SerialName("itemList")
|
||||
val itemList: List<InvoiceItem> = emptyList()
|
||||
)
|
||||
|
||||
/**
|
||||
* 发票项目明细
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceItem(
|
||||
|
||||
/**
|
||||
* 货物或服务名称
|
||||
*/
|
||||
@SerialName("goodsName")
|
||||
val goodsName: String,
|
||||
|
||||
/**
|
||||
* 税收分类编码
|
||||
*/
|
||||
@SerialName("taxClassificationCode")
|
||||
val taxClassificationCode: String,
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
@SerialName("specificationModel")
|
||||
val specificationModel: String? = null,
|
||||
|
||||
/**
|
||||
* 单位
|
||||
*/
|
||||
@SerialName("meteringUnit")
|
||||
val meteringUnit: String? = null,
|
||||
|
||||
/**
|
||||
* 数量
|
||||
*
|
||||
* 保留小数点后 8 位
|
||||
*/
|
||||
@SerialName("quantity")
|
||||
val quantity: String? = null,
|
||||
|
||||
/**
|
||||
* 单价
|
||||
*
|
||||
* 保留小数点后 8 位
|
||||
*/
|
||||
@SerialName("unitPrice")
|
||||
val unitPrice: String? = null,
|
||||
|
||||
/**
|
||||
* 含税标识
|
||||
*
|
||||
* 0:不含税
|
||||
* 1:含税
|
||||
*/
|
||||
@SerialName("taxIncludeFlag")
|
||||
val taxIncludeFlag: String,
|
||||
|
||||
/**
|
||||
* 项目金额
|
||||
*
|
||||
* 保留小数点后 2 位
|
||||
*/
|
||||
@SerialName("itemAmount")
|
||||
val itemAmount: String,
|
||||
|
||||
/**
|
||||
* 税率
|
||||
*
|
||||
* 保留小数点后 2 位,例如 0.13
|
||||
*/
|
||||
@SerialName("taxRate")
|
||||
val taxRate: String,
|
||||
|
||||
/**
|
||||
* 项目税额
|
||||
*
|
||||
* 保留小数点后 2 位
|
||||
*/
|
||||
@SerialName("taxRateAmount")
|
||||
val taxRateAmount: String,
|
||||
|
||||
/**
|
||||
* 扣除额
|
||||
*
|
||||
* 差额发票有值,保留小数点后 2 位
|
||||
*/
|
||||
@SerialName("deduction")
|
||||
val deduction: String? = null,
|
||||
|
||||
/**
|
||||
* 优惠政策标识
|
||||
*
|
||||
* 0:不使用
|
||||
* 1:使用
|
||||
*/
|
||||
@SerialName("preferentialPolicyFlag")
|
||||
val preferentialPolicyFlag: String? = null,
|
||||
|
||||
/**
|
||||
* 零税率标识
|
||||
*
|
||||
* 空:非零税率
|
||||
* 1:免税
|
||||
* 2:不征税
|
||||
* 3:普通零税率
|
||||
*/
|
||||
@SerialName("zeroTaxFlag")
|
||||
val zeroTaxFlag: String? = null,
|
||||
|
||||
/**
|
||||
* 增值税特殊管理
|
||||
*
|
||||
* 例如:免税、不征税、简易征收等
|
||||
*/
|
||||
@SerialName("vatSpecialManage")
|
||||
val vatSpecialManage: String? = null,
|
||||
|
||||
/**
|
||||
* 发票行性质
|
||||
*
|
||||
* 0:正常行
|
||||
* 1:折扣行
|
||||
* 2:被折扣行
|
||||
*/
|
||||
@SerialName("itemProperty")
|
||||
val itemProperty: String,
|
||||
|
||||
/**
|
||||
* 项目序号
|
||||
*
|
||||
* 用于表示项目明细的先后顺序
|
||||
*/
|
||||
@SerialName("itemNo")
|
||||
val itemNo: String
|
||||
)
|
||||
@@ -8,6 +8,7 @@ import com.bbit.ticket.entity.request.GetLoginSmsCodeRequest
|
||||
import com.bbit.ticket.entity.request.QueryRealNameAuthQrStatusRequest
|
||||
import com.bbit.ticket.entity.request.SmsLoginRequest
|
||||
import com.bbit.ticket.entity.request.TaxBureauAuthReq
|
||||
import com.bbit.ticket.entity.request.UpdateDigitalAccountStatusRequest
|
||||
import com.bbit.ticket.entity.request.UpdateInvoiceSettingRequest
|
||||
import com.bbit.ticket.service.piaotong.PTAuthService
|
||||
import com.bbit.ticket.service.piaotong.PTConfigService
|
||||
@@ -73,6 +74,17 @@ fun Route.registerPTAuthRoutes() {
|
||||
}
|
||||
}
|
||||
|
||||
put("/digital-accounts/{id}/status") {
|
||||
call.respondPt("更新数电账号状态失败") {
|
||||
val id = call.parameters["id"] ?: throw IllegalArgumentException("缺少数电账号ID")
|
||||
PTConfigService.updateDigitalAccountStatus(
|
||||
call.requireCurrentUser(),
|
||||
id,
|
||||
call.receive<UpdateDigitalAccountStatusRequest>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
get("/preset") {
|
||||
call.respondPtOrEmptyObject("查询预设数据失败") {
|
||||
PTConfigService.getEnterpriseInfo(call.requireCurrentUser())
|
||||
@@ -115,13 +127,17 @@ fun Route.registerPTAuthRoutes() {
|
||||
|
||||
post("/send-sms-code") {
|
||||
call.respondPt("发送登录短信验证码失败") {
|
||||
PTAuthService.sendLoginSmsCode(call.receive<GetLoginSmsCodeRequest>())
|
||||
val req = call.receive<GetLoginSmsCodeRequest>()
|
||||
PTConfigService.requireDigitalAccountForLogin(call.requireCurrentUser(), req.taxpayerNum, req.account)
|
||||
PTAuthService.sendLoginSmsCode(req)
|
||||
}
|
||||
}
|
||||
|
||||
post("/sms-login") {
|
||||
call.respondPt("短信验证码登录失败") {
|
||||
PTAuthService.smsLogin(call.receive<SmsLoginRequest>())
|
||||
val req = call.receive<SmsLoginRequest>()
|
||||
PTConfigService.requireDigitalAccountForLogin(call.requireCurrentUser(), req.taxpayerNum, req.account)
|
||||
PTAuthService.smsLogin(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.bbit.ticket.route.piaotong
|
||||
|
||||
import com.bbit.ticket.entity.common.BizException
|
||||
import com.bbit.ticket.entity.request.AskInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.InvoiceQuerySubmitRequest
|
||||
import com.bbit.ticket.entity.request.QueryInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.RedCreateRequest
|
||||
import com.bbit.ticket.service.piaotong.PTBlueService
|
||||
@@ -57,7 +58,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPt("查询发票详情失败") {
|
||||
PTBlueService.getInvoiceDetail(call.requireCurrentUser().id, invoiceReqSerialNo)
|
||||
PTBlueService.getInvoiceDetail(call.requireCurrentUser(), invoiceReqSerialNo)
|
||||
?: throw BizException("-1", "未找到该发票记录")
|
||||
}
|
||||
}
|
||||
@@ -66,7 +67,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPt("查询发票下载地址失败") {
|
||||
PTBlueService.getInvoiceDownloadUrl(call.requireCurrentUser().id, invoiceReqSerialNo)
|
||||
PTBlueService.getInvoiceDownloadUrl(call.requireCurrentUser(), invoiceReqSerialNo)
|
||||
?: throw BizException("-1", "未找到该发票记录")
|
||||
}
|
||||
}
|
||||
@@ -75,7 +76,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPtPdf("预览票样失败", "$invoiceReqSerialNo.pdf") {
|
||||
PTBlueService.getInvoicePreview(call.requireCurrentUser().id, invoiceReqSerialNo)
|
||||
PTBlueService.getInvoicePreview(call.requireCurrentUser(), invoiceReqSerialNo)
|
||||
?: throw BizException("-1", "未找到票样地址")
|
||||
}
|
||||
}
|
||||
@@ -84,7 +85,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPt("查询红票下载地址失败") {
|
||||
PTRedService.getRedInvoiceDownloadUrl(call.requireCurrentUser().id, invoiceReqSerialNo)
|
||||
PTRedService.getRedInvoiceDownloadUrl(call.requireCurrentUser(), invoiceReqSerialNo)
|
||||
?: throw BizException("-1", "未找到该红票记录")
|
||||
}
|
||||
}
|
||||
@@ -93,7 +94,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPtPdf("预览红票票样失败", "$invoiceReqSerialNo.pdf") {
|
||||
PTRedService.getRedInvoicePreview(call.requireCurrentUser().id, invoiceReqSerialNo)
|
||||
PTRedService.getRedInvoicePreview(call.requireCurrentUser(), invoiceReqSerialNo)
|
||||
?: throw BizException("-1", "未找到票样地址")
|
||||
}
|
||||
}
|
||||
@@ -120,7 +121,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPt("查询红票申请信息失败") {
|
||||
PTRedService.getRedInvoiceInfo(call.requireCurrentUser().id, invoiceReqSerialNo)
|
||||
PTRedService.getRedInvoiceInfo(call.requireCurrentUser(), invoiceReqSerialNo)
|
||||
?: throw BizException("-1", "未找到红票申请信息")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,86 +13,45 @@ import com.bbit.ticket.entity.request.TaxBureauAuthReq
|
||||
import com.bbit.ticket.entity.response.AuthQrcodeResponse
|
||||
import com.bbit.ticket.entity.response.AuthQrcodeStatusResponse
|
||||
import com.bbit.ticket.entity.response.DigitalAccountInfo
|
||||
import com.bbit.ticket.entity.response.EnterpriseTaxInfo
|
||||
import com.bbit.ticket.entity.response.LoginSmsCodeResponse
|
||||
import com.bbit.ticket.entity.response.QueryEnterpriseBankAccountResponse
|
||||
import com.bbit.ticket.entity.response.QueryEnterpriseInfoResponse
|
||||
import com.bbit.ticket.entity.response.SMSLoginResponse
|
||||
import com.bbit.ticket.entity.response.TaxBureauAccountAuthContent
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import com.bbit.ticket.utils.net.PTApi
|
||||
|
||||
object PTAuthService {
|
||||
|
||||
/**
|
||||
* 查询数电账号认证状态 2.8
|
||||
*/
|
||||
suspend fun getTaxBureauAccountAuthStatus(req: TaxBureauAuthReq): TaxBureauAccountAuthContent {
|
||||
val res =
|
||||
PTClient.ptPost<TaxBureauAuthReq, TaxBureauAccountAuthContent>("getTaxBureauAccountAuthStatus.pt", req)
|
||||
return res
|
||||
return PTApi.getTaxBureauAccountAuthStatus(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实名认证二维码 2.6
|
||||
*
|
||||
* @return AuthQrcodeResponse 包含 base64 二维码图片
|
||||
*/
|
||||
suspend fun getAuthenticationQrcode(req: AuthQrcodeRequest): AuthQrcodeResponse {
|
||||
return PTClient.ptPost<AuthQrcodeRequest, AuthQrcodeResponse>("getAuthenticationQrcode.pt", req)
|
||||
return PTApi.getAuthenticationQrcode(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询认证二维码扫码状态 2.7
|
||||
*
|
||||
* @return AuthQrcodeStatusResponse 包含 scanStatus、msg
|
||||
*/
|
||||
suspend fun queryAuthQrcodeScanStatus(req: QueryRealNameAuthQrStatusRequest): AuthQrcodeStatusResponse {
|
||||
return PTClient.ptPost<QueryRealNameAuthQrStatusRequest, AuthQrcodeStatusResponse>(
|
||||
"queryAuthQrcodeScanStatus.pt",
|
||||
req
|
||||
)
|
||||
return PTApi.queryAuthQrcodeScanStatus(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送登录短信验证码 2.4
|
||||
*
|
||||
* @return LoginSmsCodeResponse 包含脱敏手机号、有效时长
|
||||
*/
|
||||
suspend fun sendLoginSmsCode(req: GetLoginSmsCodeRequest): LoginSmsCodeResponse {
|
||||
return PTClient.ptPost<GetLoginSmsCodeRequest, LoginSmsCodeResponse>("sendLoginSmsCode.pt", req)
|
||||
return PTApi.sendLoginSmsCode(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信验证码登录 2.5
|
||||
*
|
||||
* @return SMSLoginResponse 包含登录结果
|
||||
*/
|
||||
suspend fun smsLogin(req: SmsLoginRequest): SMSLoginResponse {
|
||||
return PTClient.ptPost<SmsLoginRequest, SMSLoginResponse>("smsLogin.pt", req)
|
||||
return PTApi.smsLogin(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询企业状态 2.47
|
||||
*/
|
||||
suspend fun getEnterpriseInfo(req: QueryEnterpriseInfoRequest): QueryEnterpriseInfoResponse {
|
||||
return PTClient.ptPost<QueryEnterpriseInfoRequest, QueryEnterpriseInfoResponse>("getEnterpriseInfo.pt", req)
|
||||
return PTApi.getEnterpriseInfo(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数电账号列表 2.45
|
||||
*/
|
||||
suspend fun getListTaxBureauAccount(req: QueryDigitalAccountListRequest): List<DigitalAccountInfo> {
|
||||
return PTClient.ptPost<QueryDigitalAccountListRequest, List<DigitalAccountInfo>>(
|
||||
"listTaxBureauAccount.pt",
|
||||
req
|
||||
)
|
||||
return PTApi.listTaxBureauAccount(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询企业开户行及账号 2.49
|
||||
*/
|
||||
suspend fun queryEnterpriseBankInfo(req: QueryEnterpriseBankAccountRequest): QueryEnterpriseBankAccountResponse {
|
||||
return PTClient.ptPost<QueryEnterpriseBankAccountRequest, QueryEnterpriseBankAccountResponse>("queryEnterpriseBankInfo.pt", req)
|
||||
return PTApi.queryEnterpriseBankInfo(req)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,17 +5,19 @@ package com.bbit.ticket.service.piaotong
|
||||
import com.bbit.ticket.dao.piaotong.BlueInvoiceDao
|
||||
import com.bbit.ticket.entity.common.PageResult
|
||||
import com.bbit.ticket.entity.request.AskInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.InvoiceQueryRequest
|
||||
import com.bbit.ticket.entity.request.InvoiceQuerySubmitRequest
|
||||
import com.bbit.ticket.entity.request.QueryInvoiceRequest
|
||||
import com.bbit.ticket.entity.response.GetInvoiceInfoResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.QueryInvoiceResult
|
||||
import com.bbit.ticket.entity.response.InvoiceCreateResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceHistoryItem
|
||||
import com.bbit.ticket.entity.response.InvoiceQueryResponse
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsBytes
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.net.PTApi
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import com.bbit.ticket.utils.parseUuid
|
||||
import kotlin.uuid.Uuid
|
||||
@@ -23,7 +25,13 @@ import kotlin.uuid.Uuid
|
||||
object PTBlueService {
|
||||
|
||||
suspend fun listBatchNos(user: CurrentUser): List<String> =
|
||||
dbQuery { BlueInvoiceDao.listBatchNos(user.id) }
|
||||
dbQuery {
|
||||
BlueInvoiceDao.listBatchNos(
|
||||
user.id,
|
||||
user.enterpriseId,
|
||||
if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询票通同步发票信息(支持插入和更新)
|
||||
@@ -38,8 +46,7 @@ object PTBlueService {
|
||||
enterpriseId: Uuid? = null,
|
||||
digitalAccountId: Uuid? = null,
|
||||
): QueryInvoiceResult {
|
||||
val res = PTClient.ptPost<QueryInvoiceRequest, GetInvoiceInfoResponse>(
|
||||
"queryInvoiceInfo.pt",
|
||||
val res = PTApi.queryInvoiceInfo(
|
||||
QueryInvoiceRequest(taxpayerNum = taxpayerNum, invoiceReqSerialNo = invoiceReqSerialNo)
|
||||
)
|
||||
dbQuery { BlueInvoiceDao.upsertInvoiceInfo(userId, res, enterpriseId, digitalAccountId) }
|
||||
@@ -77,7 +84,7 @@ object PTBlueService {
|
||||
): String {
|
||||
dbQuery { BlueInvoiceDao.addInvoice(userId, req, enterpriseId, digitalAccountId) }
|
||||
try {
|
||||
PTClient.ptPost<AskInvoiceRequest, InvoiceCreateResponse>("invoiceBlue.pt", req)
|
||||
PTApi.invoiceBlue(req)
|
||||
} catch (e: Exception) {
|
||||
dbQuery { BlueInvoiceDao.markInvoiceFailed(userId, req.invoiceReqSerialNo, e.message) }
|
||||
throw e
|
||||
@@ -89,9 +96,6 @@ object PTBlueService {
|
||||
return "操作成功"
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询开票历史(支持筛选)
|
||||
*/
|
||||
suspend fun getInvoiceBlueHistory(
|
||||
user: CurrentUser,
|
||||
page: Int,
|
||||
@@ -117,10 +121,30 @@ object PTBlueService {
|
||||
* 查询发票完整详情
|
||||
*/
|
||||
suspend fun getInvoiceDetail(userId: Uuid, invoiceReqSerialNo: String): InvoiceDetailResponse? =
|
||||
dbQuery { BlueInvoiceDao.invoiceDetail(userId, invoiceReqSerialNo) }
|
||||
dbQuery { BlueInvoiceDao.invoiceDetail(userId, null, null, invoiceReqSerialNo) }
|
||||
|
||||
suspend fun getInvoiceDetail(user: CurrentUser, invoiceReqSerialNo: String): InvoiceDetailResponse? =
|
||||
dbQuery {
|
||||
BlueInvoiceDao.invoiceDetail(
|
||||
user.id,
|
||||
user.enterpriseId,
|
||||
if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
invoiceReqSerialNo,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getInvoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? =
|
||||
dbQuery { BlueInvoiceDao.invoiceDownloadUrl(userId, invoiceReqSerialNo) }
|
||||
dbQuery { BlueInvoiceDao.invoiceDownloadUrl(userId, null, null, invoiceReqSerialNo) }
|
||||
|
||||
suspend fun getInvoiceDownloadUrl(user: CurrentUser, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? =
|
||||
dbQuery {
|
||||
BlueInvoiceDao.invoiceDownloadUrl(
|
||||
user.id,
|
||||
user.enterpriseId,
|
||||
if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
invoiceReqSerialNo,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getInvoicePreview(userId: Uuid, invoiceReqSerialNo: String): ByteArray? {
|
||||
val downloadUrl = getInvoiceDownloadUrl(userId, invoiceReqSerialNo)?.downloadUrl
|
||||
@@ -129,6 +153,13 @@ object PTBlueService {
|
||||
return PTClient.client.get(downloadUrl).bodyAsBytes()
|
||||
}
|
||||
|
||||
suspend fun getInvoicePreview(user: CurrentUser, invoiceReqSerialNo: String): ByteArray? {
|
||||
val downloadUrl = getInvoiceDownloadUrl(user, invoiceReqSerialNo)?.downloadUrl
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
return PTClient.client.get(downloadUrl).bodyAsBytes()
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询并更新发票状态(复用 syncInvoiceFromPT)
|
||||
*/
|
||||
|
||||
@@ -11,12 +11,12 @@ import com.bbit.ticket.entity.request.QueryDigitalAccountListRequest
|
||||
import com.bbit.ticket.entity.request.QueryEnterpriseBankAccountRequest
|
||||
import com.bbit.ticket.entity.request.QueryEnterpriseInfoRequest
|
||||
import com.bbit.ticket.entity.request.TaxRegister
|
||||
import com.bbit.ticket.entity.request.UpdateDigitalAccountStatusRequest
|
||||
import com.bbit.ticket.entity.request.UpdateInvoiceSettingRequest
|
||||
import com.bbit.ticket.entity.response.BankInfo
|
||||
import com.bbit.ticket.entity.response.DigitalAccountInfo
|
||||
import com.bbit.ticket.entity.response.DigitalAccountManageItem
|
||||
import com.bbit.ticket.entity.response.EnterpriseManageResponse
|
||||
import com.bbit.ticket.entity.response.EtaxRegisterResponse
|
||||
import com.bbit.ticket.entity.response.OpenApiStatisticsItem
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.entity.common.BizException
|
||||
@@ -24,7 +24,7 @@ import com.bbit.ticket.entity.common.ErrorCode
|
||||
import com.bbit.ticket.service.system.PasswordService
|
||||
import com.bbit.ticket.utils.ApiKeyUtil
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import com.bbit.ticket.utils.net.PTApi
|
||||
import com.bbit.ticket.utils.parseUuid
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import kotlin.uuid.Uuid
|
||||
@@ -109,8 +109,7 @@ object PTConfigService {
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "企业信息不存在", HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
PTClient.ptPost<TaxRegister, EtaxRegisterResponse>(
|
||||
"registerUser.pt",
|
||||
PTApi.registerDigitalAccount(
|
||||
TaxRegister(
|
||||
taxpayerNum = enterprise.taxpayerNum,
|
||||
account = req.account,
|
||||
@@ -147,6 +146,28 @@ object PTConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateDigitalAccountStatus(
|
||||
user: CurrentUser,
|
||||
digitalAccountId: String,
|
||||
req: UpdateDigitalAccountStatusRequest,
|
||||
): String = dbQuery {
|
||||
if (!user.isSuperAdmin && !user.isEnterpriseAdmin) {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "无权操作数电账号", HttpStatusCode.Forbidden)
|
||||
}
|
||||
val status = req.status.trim().uppercase()
|
||||
if (status !in setOf("ENABLED", "DISABLED")) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "账号状态必须是 ENABLED 或 DISABLED")
|
||||
}
|
||||
val targetId = parseUuid(digitalAccountId, "digitalAccountId")
|
||||
val row = EnterpriseManageDao.digitalAccount(targetId)
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "数电账号不存在", HttpStatusCode.NotFound)
|
||||
if (row[PtDigitalAccountTable.enterpriseId] != requireEnterpriseId(user)) {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "无权操作该数电账号", HttpStatusCode.Forbidden)
|
||||
}
|
||||
EnterpriseManageDao.updateDigitalAccountStatus(targetId, status)
|
||||
if (status == "ENABLED") "数电账号已恢复" else "数电账号已禁用"
|
||||
}
|
||||
|
||||
private fun ensureDigitalOperatorAccount(
|
||||
enterpriseId: Uuid,
|
||||
taxpayerNum: String,
|
||||
@@ -220,6 +241,22 @@ object PTConfigService {
|
||||
if (!user.isSuperAdmin && row[PtDigitalAccountTable.enterpriseId] != requireEnterpriseId(user)) {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "无权操作该数电账号", HttpStatusCode.Forbidden)
|
||||
}
|
||||
if (row[PtDigitalAccountTable.status] != "ENABLED") {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "数电账号已禁用", HttpStatusCode.Forbidden)
|
||||
}
|
||||
EnterpriseManageDao.run { row.toDigitalAccountItem() }
|
||||
}
|
||||
|
||||
suspend fun requireDigitalAccountForLogin(user: CurrentUser, taxpayerNum: String, account: String): DigitalAccountManageItem =
|
||||
dbQuery {
|
||||
val row = EnterpriseManageDao.digitalAccountByTaxpayerAndAccount(taxpayerNum.trim(), account.trim())
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "数电账号不存在", HttpStatusCode.NotFound)
|
||||
if (!user.isSuperAdmin && row[PtDigitalAccountTable.enterpriseId] != requireEnterpriseId(user)) {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "无权操作该数电账号", HttpStatusCode.Forbidden)
|
||||
}
|
||||
if (row[PtDigitalAccountTable.status] != "ENABLED") {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "数电账号已禁用", HttpStatusCode.Forbidden)
|
||||
}
|
||||
EnterpriseManageDao.run { row.toDigitalAccountItem() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import com.bbit.ticket.dao.piaotong.RedInvoiceDao
|
||||
import com.bbit.ticket.entity.request.QuickRedInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.RedCreateRequest
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.QuickRedInvoiceResponse
|
||||
import com.bbit.ticket.entity.response.RedInvoiceInfoResponse
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.net.PTApi
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import com.bbit.ticket.utils.parseUuid
|
||||
import io.ktor.client.request.get
|
||||
@@ -25,14 +25,19 @@ import kotlin.uuid.Uuid
|
||||
object PTRedService {
|
||||
|
||||
/**
|
||||
* 红票接口调用
|
||||
* 红票接口调用 2.10
|
||||
*/
|
||||
suspend fun invoiceRed(user: CurrentUser,req:RedCreateRequest): String {
|
||||
suspend fun invoiceRed(user: CurrentUser, req: RedCreateRequest): String {
|
||||
val account = PTConfigService.requireDigitalAccountForAction(user, null)
|
||||
val invoiceReqSerialNo = PTClient.ptDate()
|
||||
val historyId = Uuid.parse(req.historyId)
|
||||
val his = dbQuery {
|
||||
HistoryDao.findByHistory(historyId, user.id)
|
||||
HistoryDao.findByHistory(
|
||||
historyId,
|
||||
user.id,
|
||||
user.enterpriseId,
|
||||
if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
)
|
||||
}
|
||||
val req = QuickRedInvoiceRequest(
|
||||
taxpayerNum = account.taxpayerNum,
|
||||
@@ -42,33 +47,57 @@ object PTRedService {
|
||||
blueAllEleInvNo = his.electronicInvoiceNo,
|
||||
blueInvoiceDate = his.blueInvoiceDate,
|
||||
redReason = req.redReason,
|
||||
amount = his.totalAmount?.negate()?.toPlainString()?:"0.0",
|
||||
amount = his.totalAmount?.negate()?.toPlainString() ?: "0.0",
|
||||
account = account.account,
|
||||
invoiceKind = his.invoiceKind,
|
||||
takerName = req.takerName,
|
||||
takerTel = req.takerTel,
|
||||
takerEmail = req.takerEmail,
|
||||
)
|
||||
PTClient.ptPost<QuickRedInvoiceRequest, QuickRedInvoiceResponse>("invoiceRed.pt", req)
|
||||
PTApi.invoiceRed(req)
|
||||
dbQuery { RedInvoiceDao.addRedInvoice(user.id, historyId, req) }
|
||||
val enterpriseId = parseUuid(account.enterpriseId, "enterpriseId")
|
||||
val digitalAccountId = parseUuid(account.id, "digitalAccountId")
|
||||
PTBlueService.syncInvoiceFromPT(user.id, his.invoiceReqSerialNo, account.taxpayerNum, enterpriseId, digitalAccountId)
|
||||
PTBlueService.syncInvoiceFromPT(user.id, invoiceReqSerialNo, account.taxpayerNum, enterpriseId, digitalAccountId)
|
||||
PTBlueService.syncInvoiceFromPT(
|
||||
user.id,
|
||||
his.invoiceReqSerialNo,
|
||||
account.taxpayerNum,
|
||||
enterpriseId,
|
||||
digitalAccountId
|
||||
)
|
||||
PTBlueService.syncInvoiceFromPT(
|
||||
user.id,
|
||||
invoiceReqSerialNo,
|
||||
account.taxpayerNum,
|
||||
enterpriseId,
|
||||
digitalAccountId
|
||||
)
|
||||
return "操作成功"
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询红票申请信息
|
||||
*/
|
||||
suspend fun getRedInvoiceInfo(userId: Uuid, invoiceReqSerialNo: String): RedInvoiceInfoResponse? =
|
||||
dbQuery { RedInvoiceDao.findRedInfoBySerialNo(userId, invoiceReqSerialNo) }
|
||||
suspend fun getRedInvoiceInfo(user: CurrentUser, invoiceReqSerialNo: String): RedInvoiceInfoResponse? =
|
||||
dbQuery {
|
||||
RedInvoiceDao.findRedInfoBySerialNo(
|
||||
user.id, user.enterpriseId, if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
invoiceReqSerialNo,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getRedInvoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? =
|
||||
dbQuery { RedInvoiceDao.invoiceDownloadUrl(userId, invoiceReqSerialNo) }
|
||||
suspend fun getRedInvoiceDownloadUrl(user: CurrentUser, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? =
|
||||
dbQuery {
|
||||
RedInvoiceDao.invoiceDownloadUrl(
|
||||
user.id,
|
||||
user.enterpriseId,
|
||||
if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
invoiceReqSerialNo,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getRedInvoicePreview(userId: Uuid, invoiceReqSerialNo: String): ByteArray? {
|
||||
val downloadUrl = getRedInvoiceDownloadUrl(userId, invoiceReqSerialNo)?.downloadUrl
|
||||
suspend fun getRedInvoicePreview(user: CurrentUser, invoiceReqSerialNo: String): ByteArray? {
|
||||
val downloadUrl = getRedInvoiceDownloadUrl(user, invoiceReqSerialNo)?.downloadUrl
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
return PTClient.client.get(downloadUrl).bodyAsBytes()
|
||||
|
||||
@@ -13,12 +13,10 @@ import com.bbit.ticket.entity.common.system.LoginRequest
|
||||
import com.bbit.ticket.entity.common.system.LoginResponse
|
||||
import com.bbit.ticket.entity.common.system.MeResponse
|
||||
import com.bbit.ticket.entity.request.EnterpriseRegisterRequest
|
||||
import com.bbit.ticket.entity.request.TaxRegisterInfo
|
||||
import com.bbit.ticket.entity.request.toTaxRegisterInfo
|
||||
import com.bbit.ticket.entity.response.EnterpriseTaxInfo
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import com.bbit.ticket.utils.net.PTApi
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
|
||||
@@ -92,7 +90,7 @@ object AuthService {
|
||||
throw BizException(ErrorCode.DATA_CONFLICT.code, "企业已存在", HttpStatusCode.Conflict)
|
||||
}
|
||||
}
|
||||
PTClient.ptPost<TaxRegisterInfo, EnterpriseTaxInfo>("register.pt", registerInfo)
|
||||
PTApi.registerEnterprise(registerInfo)
|
||||
|
||||
dbQuery {
|
||||
val enterpriseId = EnterpriseManageDao.createEnterprise(registerInfo)
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.bbit.ticket.utils.bootstrap
|
||||
|
||||
object Global {
|
||||
|
||||
val isDev = false
|
||||
val isDev = true
|
||||
|
||||
// 请求基础地址
|
||||
var baseUrl: String
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
package com.bbit.ticket.utils.bootstrap
|
||||
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseManageDao
|
||||
import com.bbit.ticket.database.piaotong.PtEnterpriseTable
|
||||
import com.bbit.ticket.database.system.*
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.entity.request.QueryEnterpriseInfoRequest
|
||||
import com.bbit.ticket.entity.request.TaxRegisterInfo
|
||||
import com.bbit.ticket.service.system.PasswordService
|
||||
import com.bbit.ticket.utils.net.PTApi
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.inList
|
||||
@@ -26,6 +31,7 @@ object SeedData {
|
||||
const val ADMIN_INIT_PASSWORD = "Admin@123456"
|
||||
|
||||
private const val DEFAULT_ORG_CODE = "DEFAULT_ORG"
|
||||
private const val DEFAULT_ADMIN_TAXPAYER_NUM = "500102201007206608"
|
||||
private const val SUPER_ADMIN_ROLE_CODE = "SUPER_ADMIN"
|
||||
private const val ENTERPRISE_ADMIN_ROLE_CODE = "ENTERPRISE_ADMIN"
|
||||
private const val DIGITAL_OPERATOR_ROLE_CODE = "DIGITAL_OPERATOR"
|
||||
@@ -38,6 +44,7 @@ object SeedData {
|
||||
val now = OffsetDateTime.now()
|
||||
val orgId = upsertDefaultOrg(now)
|
||||
val roleId = upsertSuperAdminRole(now)
|
||||
val defaultEnterpriseId = ensureDefaultAdminEnterprise()
|
||||
val enterpriseAdminRoleId = upsertBusinessRole(
|
||||
code = ENTERPRISE_ADMIN_ROLE_CODE,
|
||||
name = "企业管理员",
|
||||
@@ -50,9 +57,10 @@ object SeedData {
|
||||
description = "数电账号对应的平台开票员角色",
|
||||
now = now,
|
||||
)
|
||||
val adminId = upsertAdminUser(orgId, now)
|
||||
val adminId = upsertAdminUser(orgId, defaultEnterpriseId, now)
|
||||
upsertUserRole(adminId, roleId)
|
||||
val menuIds = upsertMenus(now)
|
||||
disableObsoleteMenus(now)
|
||||
bindRoleMenus(roleId, menuIds.values.toList())
|
||||
bindRoleMenus(enterpriseAdminRoleId, enterpriseAdminMenuIds(menuIds))
|
||||
bindRoleMenus(digitalOperatorRoleId, digitalOperatorMenuIds(menuIds))
|
||||
@@ -156,7 +164,40 @@ object SeedData {
|
||||
// Admin user
|
||||
// =========================================================
|
||||
|
||||
private suspend fun upsertAdminUser(orgId: Uuid, now: OffsetDateTime): Uuid = dbQuery {
|
||||
private suspend fun ensureDefaultAdminEnterprise(): Uuid {
|
||||
val existingId = dbQuery {
|
||||
EnterpriseManageDao.findEnterpriseByTaxpayerNum(DEFAULT_ADMIN_TAXPAYER_NUM)?.get(PtEnterpriseTable.id)
|
||||
}
|
||||
val ptEnterprise = PTApi.getEnterpriseInfo(QueryEnterpriseInfoRequest(DEFAULT_ADMIN_TAXPAYER_NUM))
|
||||
|
||||
if (existingId != null) {
|
||||
dbQuery { EnterpriseManageDao.updateEnterpriseFromPt(existingId, ptEnterprise) }
|
||||
return existingId
|
||||
}
|
||||
|
||||
val enterpriseInfo = TaxRegisterInfo(
|
||||
taxpayerNum = DEFAULT_ADMIN_TAXPAYER_NUM,
|
||||
enterpriseName = ptEnterprise.enterpriseName.ifBlank { DEFAULT_ADMIN_TAXPAYER_NUM },
|
||||
legalPersonName = null,
|
||||
contactsName = null,
|
||||
contactsEmail = null,
|
||||
contactsPhone = null,
|
||||
regionCode = ptEnterprise.regionCode,
|
||||
cityName = ptEnterprise.cityName,
|
||||
enterpriseAddress = ptEnterprise.enterpriseAddress,
|
||||
taxRegistrationCertificate = null,
|
||||
)
|
||||
|
||||
return dbQuery {
|
||||
val enterpriseId = EnterpriseManageDao.findEnterpriseByTaxpayerNum(DEFAULT_ADMIN_TAXPAYER_NUM)
|
||||
?.get(PtEnterpriseTable.id)
|
||||
?: EnterpriseManageDao.createEnterprise(enterpriseInfo)
|
||||
EnterpriseManageDao.updateEnterpriseFromPt(enterpriseId, ptEnterprise)
|
||||
enterpriseId
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun upsertAdminUser(orgId: Uuid, enterpriseId: Uuid, now: OffsetDateTime): Uuid = dbQuery {
|
||||
val existing = SysUserTable.selectAll()
|
||||
.where { (SysUserTable.username eq ADMIN_USERNAME) and SysUserTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
@@ -166,6 +207,7 @@ object SeedData {
|
||||
SysUserTable.update({ SysUserTable.id eq id }) {
|
||||
it[SysUserTable.nickname] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.enterpriseId] = enterpriseId
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
it[SysUserTable.userType] = "SYSTEM"
|
||||
it[SysUserTable.updatedAt] = now
|
||||
@@ -180,6 +222,7 @@ object SeedData {
|
||||
it[SysUserTable.passwordHash] = PasswordService.hash(ADMIN_INIT_PASSWORD)
|
||||
it[SysUserTable.nickname] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.enterpriseId] = enterpriseId
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
it[SysUserTable.userType] = "SYSTEM"
|
||||
it[SysUserTable.tokenVersion] = 1
|
||||
@@ -234,15 +277,15 @@ object SeedData {
|
||||
catalog("logs", "日志管理", "LogsRoot", "Logs", 30),
|
||||
subMenu("logs_operation", "logs", "操作日志", "LogsOperation", "/logs/operation", "logs/operation/index", "ScrollText", "log:operation:view", 10),
|
||||
subMenu("logs_api_access", "logs", "接口日志", "LogsApiAccess", "/logs/api-access", "logs/api-access/index", "Waypoints", "log:api-access:view", 20),
|
||||
subMenu("openapi_statistics", "logs", "OpenAPI", "OpenApiStatistics", "/logs/openapi", "statistics/openapi/index", "Waypoints", "openapi:statistics:view", 30),
|
||||
catalog("basic_info", "基础信息", "BasicInfoRoot", "Building2", 40),
|
||||
subMenu("enterprise_info", "basic_info", "企业信息", "EnterpriseInfo", "/enterprise/info", "piaotong/index", "Building2", "enterprise:info:view", 10),
|
||||
subMenu("digital_account", "basic_info", "数电账号", "DigitalAccountManage", "/enterprise/digital-accounts", "piaotong/digital-accounts/index", "Users", "digital-account:view", 20),
|
||||
button("digital_account_status", "digital_account", "禁用/恢复数电账号", "DigitalAccountStatus", "digital-account:status", 1),
|
||||
subMenu("invoice_setting", "basic_info", "开票设置", "InvoiceSetting", "/enterprise/invoice-setting", "piaotong/invoice-setting/index", "SlidersHorizontal", "invoice-setting:view", 30),
|
||||
catalog("invoice_service", "开票服务", "InvoiceServiceRoot", "Receipt", 50),
|
||||
subMenu("piaotong_invoice_issue", "invoice_service", "开具蓝票", "PiaoTongInvoiceIssue", "/piaotong/invoice-issue", "piaotong/invoice-issue/index", "FilePlus", "piaotong:invoice-issue:view", 10),
|
||||
subMenu("piaotong_invoice_history", "invoice_service", "开票历史", "PiaoTongInvoiceHistory", "/piaotong/invoice-history", "piaotong/invoice-history/index", "History", "piaotong:invoice-history:view", 20),
|
||||
catalog("statistics_info", "统计信息", "StatisticsInfoRoot", "ChartNoAxesColumn", 60),
|
||||
subMenu("openapi_statistics", "statistics_info", "OpenAPI", "OpenApiStatistics", "/statistics/openapi", "statistics/openapi/index", "Waypoints", "openapi:statistics:view", 10),
|
||||
)
|
||||
|
||||
val idMap = mutableMapOf<String, Uuid>()
|
||||
@@ -260,11 +303,11 @@ object SeedData {
|
||||
"basic_info",
|
||||
"enterprise_info",
|
||||
"digital_account",
|
||||
"digital_account_status",
|
||||
"invoice_setting",
|
||||
"invoice_service",
|
||||
"piaotong_invoice_issue",
|
||||
"piaotong_invoice_history",
|
||||
"statistics_info",
|
||||
"openapi_statistics",
|
||||
).mapNotNull { menuIds[it] }
|
||||
|
||||
@@ -276,7 +319,6 @@ object SeedData {
|
||||
"invoice_service",
|
||||
"piaotong_invoice_issue",
|
||||
"piaotong_invoice_history",
|
||||
"statistics_info",
|
||||
"openapi_statistics",
|
||||
).mapNotNull { menuIds[it] }
|
||||
|
||||
@@ -351,6 +393,16 @@ object SeedData {
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun disableObsoleteMenus(now: OffsetDateTime) = dbQuery {
|
||||
SysMenuTable.update({
|
||||
(SysMenuTable.name inList listOf("StatisticsInfoRoot")) and SysMenuTable.deletedAt.isNull()
|
||||
}) {
|
||||
it[SysMenuTable.visible] = false
|
||||
it[SysMenuTable.status] = "DISABLED"
|
||||
it[SysMenuTable.updatedAt] = now
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Dicts
|
||||
// =========================================================
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.bbit.ticket.utils.net
|
||||
|
||||
import com.bbit.ticket.entity.request.AskInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.AuthQrcodeRequest
|
||||
import com.bbit.ticket.entity.request.GetLoginSmsCodeRequest
|
||||
import com.bbit.ticket.entity.request.InvoiceQueryRequest
|
||||
import com.bbit.ticket.entity.request.QueryDigitalAccountListRequest
|
||||
import com.bbit.ticket.entity.request.QueryEnterpriseBankAccountRequest
|
||||
import com.bbit.ticket.entity.request.QueryEnterpriseInfoRequest
|
||||
import com.bbit.ticket.entity.request.QueryInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.QueryRealNameAuthQrStatusRequest
|
||||
import com.bbit.ticket.entity.request.QuickRedInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.SmsLoginRequest
|
||||
import com.bbit.ticket.entity.request.TaxBureauAuthReq
|
||||
import com.bbit.ticket.entity.request.TaxRegister
|
||||
import com.bbit.ticket.entity.request.TaxRegisterInfo
|
||||
import com.bbit.ticket.entity.response.AuthQrcodeResponse
|
||||
import com.bbit.ticket.entity.response.AuthQrcodeStatusResponse
|
||||
import com.bbit.ticket.entity.response.DigitalAccountInfo
|
||||
import com.bbit.ticket.entity.response.EnterpriseTaxInfo
|
||||
import com.bbit.ticket.entity.response.EtaxRegisterResponse
|
||||
import com.bbit.ticket.entity.response.GetInvoiceInfoResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceCreateResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceQueryResponse
|
||||
import com.bbit.ticket.entity.response.LoginSmsCodeResponse
|
||||
import com.bbit.ticket.entity.response.QueryEnterpriseBankAccountResponse
|
||||
import com.bbit.ticket.entity.response.QueryEnterpriseInfoResponse
|
||||
import com.bbit.ticket.entity.response.QuickRedInvoiceResponse
|
||||
import com.bbit.ticket.entity.response.SMSLoginResponse
|
||||
import com.bbit.ticket.entity.response.TaxBureauAccountAuthContent
|
||||
|
||||
object PTApi {
|
||||
/** 企业注册 2.2*/
|
||||
suspend fun registerEnterprise(req: TaxRegisterInfo): EnterpriseTaxInfo =
|
||||
PTClient.ptPost("register.pt", req)
|
||||
|
||||
/** 登记数电账号 2.3*/
|
||||
suspend fun registerDigitalAccount(req: TaxRegister): EtaxRegisterResponse =
|
||||
PTClient.ptPost("registerUser.pt", req)
|
||||
|
||||
/** 发送登录短信验证码 2.4 */
|
||||
suspend fun sendLoginSmsCode(req: GetLoginSmsCodeRequest): LoginSmsCodeResponse =
|
||||
PTClient.ptPost("sendLoginSmsCode.pt", req)
|
||||
|
||||
/** 短信验证码登录 2.5 */
|
||||
suspend fun smsLogin(req: SmsLoginRequest): SMSLoginResponse =
|
||||
PTClient.ptPost("smsLogin.pt", req)
|
||||
|
||||
/** 获取实名认证二维码 2.6 */
|
||||
suspend fun getAuthenticationQrcode(req: AuthQrcodeRequest): AuthQrcodeResponse =
|
||||
PTClient.ptPost("getAuthenticationQrcode.pt", req)
|
||||
|
||||
/** 查询认证二维码扫码状态 2.7 */
|
||||
suspend fun queryAuthQrcodeScanStatus(req: QueryRealNameAuthQrStatusRequest): AuthQrcodeStatusResponse =
|
||||
PTClient.ptPost("queryAuthQrcodeScanStatus.pt", req)
|
||||
|
||||
/** 查询数电账号认证状态 2.8 */
|
||||
suspend fun getTaxBureauAccountAuthStatus(req: TaxBureauAuthReq): TaxBureauAccountAuthContent =
|
||||
PTClient.ptPost("getTaxBureauAccountAuthStatus.pt", req)
|
||||
|
||||
/** 蓝票接口调用 2.9 */
|
||||
suspend fun invoiceBlue(req: AskInvoiceRequest): InvoiceCreateResponse =
|
||||
PTClient.ptPost("invoiceBlue.pt", req)
|
||||
|
||||
/** 红票接口调用 2.10 */
|
||||
suspend fun invoiceRed(req: QuickRedInvoiceRequest): QuickRedInvoiceResponse =
|
||||
PTClient.ptPost("invoiceRed.pt", req)
|
||||
|
||||
/** 开票统计及授信额度查询 2.20 */
|
||||
suspend fun queryAllEleBlueInvStatistics(req: InvoiceQueryRequest): InvoiceQueryResponse =
|
||||
PTClient.ptPost("queryAllEleBlueInvStatistics.pt", req)
|
||||
|
||||
/** 查询票通同步发票信息 2.12*/
|
||||
suspend fun queryInvoiceInfo(req: QueryInvoiceRequest): GetInvoiceInfoResponse =
|
||||
PTClient.ptPost("queryInvoiceInfo.pt", req)
|
||||
|
||||
/** 查询数电账号列表 2.45 */
|
||||
suspend fun listTaxBureauAccount(req: QueryDigitalAccountListRequest): List<DigitalAccountInfo> =
|
||||
PTClient.ptPost("listTaxBureauAccount.pt", req)
|
||||
|
||||
/** 查询企业状态 2.47 */
|
||||
suspend fun getEnterpriseInfo(req: QueryEnterpriseInfoRequest): QueryEnterpriseInfoResponse =
|
||||
PTClient.ptPost("getEnterpriseInfo.pt", req)
|
||||
|
||||
/** 查询企业开户行及账号 2.49 */
|
||||
suspend fun queryEnterpriseBankInfo(req: QueryEnterpriseBankAccountRequest): QueryEnterpriseBankAccountResponse =
|
||||
PTClient.ptPost("queryEnterpriseBankInfo.pt", req)
|
||||
|
||||
}
|
||||
@@ -172,6 +172,10 @@ export function createDigitalAccountApi(
|
||||
return http.post('/pt/digital-accounts', payload)
|
||||
}
|
||||
|
||||
export function updateDigitalAccountStatusApi(id: string, status: string): Promise<string> {
|
||||
return http.put(`/pt/digital-accounts/${id}/status`, { status })
|
||||
}
|
||||
|
||||
export interface PresetData {
|
||||
bankName: string
|
||||
bankAccount: string
|
||||
|
||||
@@ -110,6 +110,7 @@ import {
|
||||
NIcon,
|
||||
NInput,
|
||||
NModal,
|
||||
NPopconfirm,
|
||||
NSelect,
|
||||
NSpace,
|
||||
NTag,
|
||||
@@ -122,7 +123,8 @@ import {
|
||||
listDigitalAccountsApi,
|
||||
refreshDigitalAccountsApi,
|
||||
sendLoginSmsCodeApi,
|
||||
smsLoginApi
|
||||
smsLoginApi,
|
||||
updateDigitalAccountStatusApi
|
||||
} from '@/api/piaotong'
|
||||
import type { DigitalAccountItem } from '@/api/piaotong'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
@@ -144,6 +146,7 @@ const selected = ref<DigitalAccountItem | null>(null)
|
||||
const createFormRef = ref<FormInst | null>(null)
|
||||
|
||||
const canManage = computed(() => authStore.user?.userType !== 'DIGITAL_OPERATOR')
|
||||
const canUpdateStatus = computed(() => authStore.hasPermission('digital-account:status'))
|
||||
|
||||
const createForm = reactive({
|
||||
account: '',
|
||||
@@ -197,6 +200,17 @@ const columns: DataTableColumns<DigitalAccountItem> = [
|
||||
)
|
||||
},
|
||||
{ title: '最近认证', key: 'lastAuthSuccTime', minWidth: 160 },
|
||||
{
|
||||
title: '平台状态',
|
||||
key: 'status',
|
||||
width: 110,
|
||||
render: (row) =>
|
||||
h(
|
||||
NTag,
|
||||
{ type: row.status === 'ENABLED' ? 'success' : 'warning' },
|
||||
{ default: () => (row.status === 'ENABLED' ? '可用' : '禁用') }
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'API Key',
|
||||
key: 'apiKey',
|
||||
@@ -207,19 +221,49 @@ const columns: DataTableColumns<DigitalAccountItem> = [
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
fixed: 'right',
|
||||
width: 220,
|
||||
width: 300,
|
||||
render: (row) =>
|
||||
h('div', { class: 'row-actions' }, [
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', secondary: true, onClick: () => openSms(row) },
|
||||
{
|
||||
size: 'small',
|
||||
secondary: true,
|
||||
disabled: row.status !== 'ENABLED',
|
||||
onClick: () => openSms(row)
|
||||
},
|
||||
{ icon: () => h(NIcon, { component: KeyRound }), default: () => '短信登录' }
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', tertiary: true, onClick: () => openQrcode(row) },
|
||||
{
|
||||
size: 'small',
|
||||
tertiary: true,
|
||||
disabled: row.status !== 'ENABLED',
|
||||
onClick: () => openQrcode(row)
|
||||
},
|
||||
{ icon: () => h(NIcon, { component: ShieldCheck }), default: () => '认证' }
|
||||
),
|
||||
canUpdateStatus.value
|
||||
? h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => updateAccountStatus(row)
|
||||
},
|
||||
{
|
||||
trigger: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', tertiary: true, type: row.status === 'ENABLED' ? 'warning' : 'success' },
|
||||
{ default: () => (row.status === 'ENABLED' ? '禁用' : '恢复') }
|
||||
),
|
||||
default: () =>
|
||||
row.status === 'ENABLED'
|
||||
? `确认禁用数电账号 ${row.account} 吗?`
|
||||
: `确认恢复数电账号 ${row.account} 吗?`
|
||||
}
|
||||
)
|
||||
: null
|
||||
])
|
||||
}
|
||||
]
|
||||
@@ -270,6 +314,13 @@ async function createAccount() {
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAccountStatus(row: DigitalAccountItem) {
|
||||
const nextStatus = row.status === 'ENABLED' ? 'DISABLED' : 'ENABLED'
|
||||
await updateDigitalAccountStatusApi(row.id, nextStatus)
|
||||
message.success(nextStatus === 'ENABLED' ? '数电账号已恢复' : '数电账号已禁用')
|
||||
await load()
|
||||
}
|
||||
|
||||
async function openSms(row: DigitalAccountItem) {
|
||||
selected.value = row
|
||||
smsCode.value = ''
|
||||
|
||||
@@ -400,7 +400,6 @@
|
||||
style="color: #e74c3c; font-size: 12px"
|
||||
>农产品开票:销方信息代表实际的购方信息</span
|
||||
>
|
||||
<span v-else class="section-hint">不填则使用平台默认配置</span>
|
||||
</div>
|
||||
<div class="section-body">
|
||||
<n-grid item-responsive responsive="screen" cols="2 s:2 m:3 l:4 xl:5 2xl:6" :x-gap="12" :y-gap="4">
|
||||
|
||||
@@ -102,26 +102,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="detail-section-title">税务信息</div>
|
||||
<div class="detail-grid">
|
||||
<div v-for="item in taxDetailItems" :key="item.label" class="detail-item">
|
||||
<span>{{ item.label }}</span>
|
||||
<strong>{{ item.value }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="detail-section-title">银行与预设信息</div>
|
||||
<div class="detail-grid">
|
||||
<div v-for="item in presetDetailItems" :key="item.label" class="detail-item">
|
||||
<span>{{ item.label }}</span>
|
||||
<strong>{{ item.value }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<div class="detail-section-title">系统信息</div>
|
||||
<div class="detail-grid">
|
||||
@@ -342,36 +322,6 @@ const baseDetailItems = computed(() => {
|
||||
]
|
||||
})
|
||||
|
||||
const taxDetailItems = computed(() => {
|
||||
const user = detailUser.value
|
||||
if (!user) return []
|
||||
return [
|
||||
{ label: '纳税人识别号', value: displayValue(user.taxpayerNum) },
|
||||
{ label: '数电账号', value: displayValue(user.account) },
|
||||
{ label: '身份类型', value: displayValue(user.taxIdentityType) },
|
||||
{ label: '联系人', value: displayValue(user.taxContactName) },
|
||||
{ label: '联系人电话', value: displayValue(user.taxContactPhone) },
|
||||
{ label: '联系人邮箱', value: displayValue(user.taxContactEmail) },
|
||||
{ label: '法定代表人', value: displayValue(user.taxLegalPersonName) },
|
||||
{ label: '企业名称', value: displayValue(user.taxEnterpriseName) },
|
||||
{ label: '地区编码', value: displayValue(user.taxRegionCode) },
|
||||
{ label: '城市', value: displayValue(user.taxCityName) },
|
||||
{ label: '企业地址', value: displayValue(user.taxEnterpriseAddress) },
|
||||
{ label: '税务登记证', value: displayValue(user.taxRegistrationCertificate) }
|
||||
]
|
||||
})
|
||||
|
||||
const presetDetailItems = computed(() => {
|
||||
const user = detailUser.value
|
||||
if (!user) return []
|
||||
return [
|
||||
{ label: '开户行', value: displayValue(user.bankName) },
|
||||
{ label: '银行账号', value: displayValue(user.bankAccount) },
|
||||
{ label: '预设地址', value: displayValue(user.presetAddress) },
|
||||
{ label: '预设电话', value: displayValue(user.presetPhone) }
|
||||
]
|
||||
})
|
||||
|
||||
const systemDetailItems = computed(() => {
|
||||
const user = detailUser.value
|
||||
if (!user) return []
|
||||
|
||||
@@ -36,22 +36,6 @@ export interface UserDetail {
|
||||
deletedAt?: string | null
|
||||
deletedBy?: string | null
|
||||
version: number
|
||||
taxpayerNum?: string | null
|
||||
account?: string | null
|
||||
taxIdentityType?: string | null
|
||||
taxContactName?: string | null
|
||||
taxContactPhone?: string | null
|
||||
taxContactEmail?: string | null
|
||||
taxLegalPersonName?: string | null
|
||||
taxEnterpriseName?: string | null
|
||||
taxRegionCode?: string | null
|
||||
taxCityName?: string | null
|
||||
taxEnterpriseAddress?: string | null
|
||||
taxRegistrationCertificate?: string | null
|
||||
bankName?: string | null
|
||||
bankAccount?: string | null
|
||||
presetAddress?: string | null
|
||||
presetPhone?: string | null
|
||||
}
|
||||
|
||||
export interface UserRoleBrief {
|
||||
@@ -90,10 +74,6 @@ export interface UpdateUserRequest {
|
||||
email?: string
|
||||
avatar?: string
|
||||
orgId?: string
|
||||
taxpayerNum?: string
|
||||
account?: string
|
||||
taxPassword?: string
|
||||
taxIdentityType?: string
|
||||
}
|
||||
|
||||
export interface UpdateUserStatusRequest {
|
||||
|
||||
Reference in New Issue
Block a user