多数电账号管理
This commit is contained in:
@@ -61,6 +61,8 @@ object BlueInvoiceDao {
|
||||
*/
|
||||
fun invoiceHistory(
|
||||
userId: Uuid,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
invoiceType: String? = null,
|
||||
@@ -68,7 +70,13 @@ object BlueInvoiceDao {
|
||||
batchNo: String? = null,
|
||||
): PageResult<InvoiceHistoryItem> {
|
||||
val conditions = mutableListOf<Op<Boolean>>()
|
||||
conditions.add(HistoryInvoiceBasicTable.userId eq userId)
|
||||
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)
|
||||
}
|
||||
conditions.add(HistoryInvoiceBasicTable.deletedAt.isNull())
|
||||
|
||||
// 发票类型筛选:前端传 BLUE/RED,库中存 1/2
|
||||
@@ -147,12 +155,14 @@ object BlueInvoiceDao {
|
||||
return PageResult(items, page, pageSize, total)
|
||||
}
|
||||
|
||||
fun addInvoice(userId: Uuid, req: AskInvoiceRequest) {
|
||||
fun addInvoice(userId: Uuid, req: AskInvoiceRequest, enterpriseId: Uuid?, digitalAccountId: Uuid?) {
|
||||
val now = OffsetDateTime.now()
|
||||
|
||||
// 1. 插入 HistoryInvoiceBasicTable(基本信息历史快照)
|
||||
val basicRow = HistoryInvoiceBasicTable.insert {
|
||||
it[HistoryInvoiceBasicTable.userId] = userId
|
||||
it[HistoryInvoiceBasicTable.enterpriseId] = enterpriseId
|
||||
it[HistoryInvoiceBasicTable.digitalAccountId] = digitalAccountId
|
||||
it[HistoryInvoiceBasicTable.invoiceReqSerialNo] = req.invoiceReqSerialNo
|
||||
|
||||
// ---- 状态 ----
|
||||
@@ -295,7 +305,7 @@ object BlueInvoiceDao {
|
||||
* - 记录不存在 → 插入(由查询结果反推基础数据)
|
||||
* - 商品明细采用先删后插策略
|
||||
*/
|
||||
fun upsertInvoiceInfo(userId: Uuid, req: GetInvoiceInfoResponse) {
|
||||
fun upsertInvoiceInfo(userId: Uuid, req: GetInvoiceInfoResponse, enterpriseId: Uuid?, digitalAccountId: Uuid?) {
|
||||
transaction {
|
||||
val now = OffsetDateTime.now()
|
||||
val existingId = HistoryInvoiceBasicTable
|
||||
@@ -307,6 +317,8 @@ object BlueInvoiceDao {
|
||||
val invoiceId = if (existingId != null) {
|
||||
// 更新已有记录
|
||||
HistoryInvoiceBasicTable.update({ HistoryInvoiceBasicTable.id eq existingId }) {
|
||||
if (enterpriseId != null) it[HistoryInvoiceBasicTable.enterpriseId] = enterpriseId
|
||||
if (digitalAccountId != null) it[HistoryInvoiceBasicTable.digitalAccountId] = digitalAccountId
|
||||
applySyncFields(it, req)
|
||||
it[HistoryInvoiceBasicTable.updatedAt] = now
|
||||
}
|
||||
@@ -315,6 +327,8 @@ object BlueInvoiceDao {
|
||||
// 插入新记录
|
||||
HistoryInvoiceBasicTable.insert {
|
||||
it[HistoryInvoiceBasicTable.userId] = userId
|
||||
it[HistoryInvoiceBasicTable.enterpriseId] = enterpriseId
|
||||
it[HistoryInvoiceBasicTable.digitalAccountId] = digitalAccountId
|
||||
it[HistoryInvoiceBasicTable.invoiceReqSerialNo] = req.invoiceReqSerialNo
|
||||
applySyncFields(it, req)
|
||||
it[HistoryInvoiceBasicTable.createdAt] = now
|
||||
@@ -449,6 +463,25 @@ object BlueInvoiceDao {
|
||||
?: throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "发票记录不存在用户信息")
|
||||
}
|
||||
|
||||
fun findInvoiceScopeBySerialNo(invoiceReqSerialNo: String): InvoiceScope {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where { HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo }
|
||||
.singleOrNull()
|
||||
?: throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "发票记录不存在")
|
||||
return InvoiceScope(
|
||||
userId = row[HistoryInvoiceBasicTable.userId]
|
||||
?: throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "发票记录不存在用户信息"),
|
||||
enterpriseId = row[HistoryInvoiceBasicTable.enterpriseId],
|
||||
digitalAccountId = row[HistoryInvoiceBasicTable.digitalAccountId],
|
||||
)
|
||||
}
|
||||
|
||||
data class InvoiceScope(
|
||||
val userId: Uuid,
|
||||
val enterpriseId: Uuid?,
|
||||
val digitalAccountId: Uuid?,
|
||||
)
|
||||
|
||||
fun findRelatedInvoiceReqSerialNos(userId: Uuid, invoiceReqSerialNo: String): List<String> {
|
||||
val basicRow = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
|
||||
package com.bbit.ticket.dao.piaotong
|
||||
|
||||
import com.bbit.ticket.database.piaotong.PtDigitalAccountTable
|
||||
import com.bbit.ticket.database.piaotong.PtEnterpriseTable
|
||||
import com.bbit.ticket.database.system.SysApiAccessLogTable
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.entity.request.TaxRegisterInfo
|
||||
import com.bbit.ticket.entity.request.UpdateInvoiceSettingRequest
|
||||
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.OpenApiStatisticsItem
|
||||
import com.bbit.ticket.entity.response.QueryEnterpriseInfoResponse
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.isNull
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.update
|
||||
import java.time.OffsetDateTime
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object EnterpriseManageDao {
|
||||
fun findEnterpriseByTaxpayerNum(taxpayerNum: String): ResultRow? =
|
||||
PtEnterpriseTable.selectAll()
|
||||
.where { (PtEnterpriseTable.taxpayerNum eq taxpayerNum) and PtEnterpriseTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
|
||||
fun createEnterprise(req: TaxRegisterInfo): Uuid {
|
||||
val now = OffsetDateTime.now()
|
||||
return PtEnterpriseTable.insert {
|
||||
it[taxpayerNum] = req.taxpayerNum.trim()
|
||||
it[enterpriseName] = req.enterpriseName.trim()
|
||||
it[legalPersonName] = req.legalPersonName?.trim()?.ifBlank { null }
|
||||
it[contactsName] = req.contactsName?.trim()?.ifBlank { null }
|
||||
it[contactsEmail] = req.contactsEmail?.trim()?.ifBlank { null }
|
||||
it[contactsPhone] = req.contactsPhone?.trim()?.ifBlank { null }
|
||||
it[regionCode] = req.regionCode.trim().ifBlank { null }
|
||||
it[cityName] = req.cityName.trim().ifBlank { null }
|
||||
it[enterpriseAddress] = req.enterpriseAddress?.trim()?.ifBlank { null }
|
||||
it[taxRegistrationCertificate] = req.taxRegistrationCertificate?.trim()?.ifBlank { null }
|
||||
it[createdAt] = now
|
||||
}[PtEnterpriseTable.id]
|
||||
}
|
||||
|
||||
fun enterpriseDetail(enterpriseId: Uuid): EnterpriseManageResponse? =
|
||||
PtEnterpriseTable.selectAll()
|
||||
.where { (PtEnterpriseTable.id eq enterpriseId) and PtEnterpriseTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
?.toEnterpriseResponse()
|
||||
|
||||
fun updateEnterpriseFromPt(enterpriseId: Uuid, res: QueryEnterpriseInfoResponse) {
|
||||
PtEnterpriseTable.update({ PtEnterpriseTable.id eq enterpriseId }) {
|
||||
it[taxpayerNum] = res.taxpayerNum
|
||||
it[enterpriseName] = res.enterpriseName
|
||||
it[regionCode] = res.regionCode
|
||||
it[cityName] = res.cityName
|
||||
it[enterpriseAddress] = res.enterpriseAddress
|
||||
it[invitationCode] = res.invitationCode
|
||||
it[reviewStatus] = res.reviewStatus
|
||||
it[reviewOpinion] = res.reviewOpinion
|
||||
it[invoiceKind] = res.invoiceKind
|
||||
it[invoiceLayoutFileType] = res.invoiceLayoutFileType
|
||||
it[serviceStatus] = res.serviceStatus
|
||||
it[updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateInvoiceSetting(enterpriseId: Uuid, req: UpdateInvoiceSettingRequest) {
|
||||
PtEnterpriseTable.update({ PtEnterpriseTable.id eq enterpriseId }) {
|
||||
it[bankName] = req.bankName.trim().ifBlank { null }
|
||||
it[bankAccount] = req.bankAccount.trim().ifBlank { null }
|
||||
it[presetAddress] = req.address.trim().ifBlank { null }
|
||||
it[presetPhone] = req.phone.trim().ifBlank { null }
|
||||
it[updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun digitalAccountsForEnterprise(enterpriseId: Uuid): List<DigitalAccountManageItem> =
|
||||
PtDigitalAccountTable.selectAll()
|
||||
.where { (PtDigitalAccountTable.enterpriseId eq enterpriseId) and PtDigitalAccountTable.deletedAt.isNull() }
|
||||
.orderBy(PtDigitalAccountTable.createdAt, SortOrder.DESC)
|
||||
.map { it.toDigitalAccountItem() }
|
||||
|
||||
fun digitalAccount(id: Uuid): ResultRow? =
|
||||
PtDigitalAccountTable.selectAll()
|
||||
.where { (PtDigitalAccountTable.id eq id) and PtDigitalAccountTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
|
||||
fun digitalAccountByApiKey(apiKey: String): ResultRow? =
|
||||
PtDigitalAccountTable.selectAll()
|
||||
.where {
|
||||
(PtDigitalAccountTable.apiKey eq apiKey) and
|
||||
(PtDigitalAccountTable.status eq "ENABLED") and
|
||||
PtDigitalAccountTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
fun digitalAccountForUser(userId: Uuid): ResultRow? =
|
||||
PtDigitalAccountTable.selectAll()
|
||||
.where {
|
||||
(PtDigitalAccountTable.platformUserId eq userId) and
|
||||
(PtDigitalAccountTable.status eq "ENABLED") and
|
||||
PtDigitalAccountTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
fun upsertDigitalAccount(enterpriseId: Uuid, item: DigitalAccountInfo): Uuid {
|
||||
val existing = PtDigitalAccountTable.selectAll()
|
||||
.where {
|
||||
(PtDigitalAccountTable.enterpriseId eq enterpriseId) and
|
||||
(PtDigitalAccountTable.account eq item.account) and
|
||||
PtDigitalAccountTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
val now = OffsetDateTime.now()
|
||||
if (existing != null) {
|
||||
val id = existing[PtDigitalAccountTable.id]
|
||||
PtDigitalAccountTable.update({ PtDigitalAccountTable.id eq id }) {
|
||||
it[taxpayerNum] = item.taxpayerNum
|
||||
it[name] = item.name
|
||||
it[identityType] = item.identityType
|
||||
it[operationProposed] = item.operationProposed
|
||||
it[authStatus] = item.authStatus
|
||||
it[switchable] = item.switchable
|
||||
it[wechatUserBindStatus] = item.wechatUserBindStatus
|
||||
it[lastAuthSuccTime] = item.lastAuthSuccTime
|
||||
it[loginAuthStatus] = item.loginAuthStatus
|
||||
it[lastLoginAuthTime] = item.lastLoginAuthTime
|
||||
it[riskAuthStatus] = item.riskAuthStatus
|
||||
it[lastRiskAuthTime] = item.lastRiskAuthTime
|
||||
it[updatedAt] = now
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
return PtDigitalAccountTable.insert {
|
||||
it[PtDigitalAccountTable.enterpriseId] = enterpriseId
|
||||
it[taxpayerNum] = item.taxpayerNum
|
||||
it[account] = item.account
|
||||
it[name] = item.name
|
||||
it[identityType] = item.identityType
|
||||
it[operationProposed] = item.operationProposed
|
||||
it[authStatus] = item.authStatus
|
||||
it[switchable] = item.switchable
|
||||
it[wechatUserBindStatus] = item.wechatUserBindStatus
|
||||
it[lastAuthSuccTime] = item.lastAuthSuccTime
|
||||
it[loginAuthStatus] = item.loginAuthStatus
|
||||
it[lastLoginAuthTime] = item.lastLoginAuthTime
|
||||
it[riskAuthStatus] = item.riskAuthStatus
|
||||
it[lastRiskAuthTime] = item.lastRiskAuthTime
|
||||
it[createdAt] = now
|
||||
}[PtDigitalAccountTable.id]
|
||||
}
|
||||
|
||||
fun createDigitalAccount(
|
||||
enterpriseId: Uuid,
|
||||
taxpayerNum: String,
|
||||
account: String,
|
||||
name: String,
|
||||
identityType: String,
|
||||
platformUserId: Uuid,
|
||||
apiKey: String,
|
||||
): Uuid {
|
||||
val existing = PtDigitalAccountTable.selectAll()
|
||||
.where {
|
||||
(PtDigitalAccountTable.enterpriseId eq enterpriseId) and
|
||||
(PtDigitalAccountTable.account eq account) and
|
||||
PtDigitalAccountTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
val now = OffsetDateTime.now()
|
||||
if (existing != null) {
|
||||
val id = existing[PtDigitalAccountTable.id]
|
||||
PtDigitalAccountTable.update({ PtDigitalAccountTable.id eq id }) {
|
||||
it[PtDigitalAccountTable.name] = name
|
||||
it[PtDigitalAccountTable.identityType] = identityType
|
||||
it[PtDigitalAccountTable.platformUserId] = platformUserId
|
||||
it[PtDigitalAccountTable.apiKey] = apiKey
|
||||
it[updatedAt] = now
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
return PtDigitalAccountTable.insert {
|
||||
it[PtDigitalAccountTable.enterpriseId] = enterpriseId
|
||||
it[PtDigitalAccountTable.taxpayerNum] = taxpayerNum
|
||||
it[PtDigitalAccountTable.account] = account
|
||||
it[PtDigitalAccountTable.name] = name
|
||||
it[PtDigitalAccountTable.identityType] = identityType
|
||||
it[PtDigitalAccountTable.platformUserId] = platformUserId
|
||||
it[PtDigitalAccountTable.apiKey] = apiKey
|
||||
it[PtDigitalAccountTable.createdAt] = now
|
||||
}[PtDigitalAccountTable.id]
|
||||
}
|
||||
|
||||
fun bindDigitalAccountUser(digitalAccountId: Uuid, userId: Uuid) {
|
||||
PtDigitalAccountTable.update({ PtDigitalAccountTable.id eq digitalAccountId }) {
|
||||
it[platformUserId] = userId
|
||||
it[updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.digitalAccountId] = digitalAccountId
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun bindDigitalAccountUserAndApiKey(digitalAccountId: Uuid, userId: Uuid, apiKey: String) {
|
||||
PtDigitalAccountTable.update({ PtDigitalAccountTable.id eq digitalAccountId }) {
|
||||
it[platformUserId] = userId
|
||||
it[PtDigitalAccountTable.apiKey] = apiKey
|
||||
it[updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.digitalAccountId] = digitalAccountId
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun openApiStatistics(enterpriseId: Uuid, digitalAccountId: Uuid?): List<OpenApiStatisticsItem> {
|
||||
var where = SysApiAccessLogTable.enterpriseId eq enterpriseId
|
||||
if (digitalAccountId != null) {
|
||||
where = where and (SysApiAccessLogTable.digitalAccountId eq digitalAccountId)
|
||||
}
|
||||
|
||||
return SysApiAccessLogTable
|
||||
.selectAll()
|
||||
.where { where }
|
||||
.toList()
|
||||
.groupBy { row -> row[SysApiAccessLogTable.digitalAccountId] to row[SysApiAccessLogTable.interfaceCode] }
|
||||
.map { (key, rows) ->
|
||||
OpenApiStatisticsItem(
|
||||
digitalAccountId = key.first?.toString(),
|
||||
interfaceCode = key.second,
|
||||
total = rows.size.toLong(),
|
||||
success = rows.count { it[SysApiAccessLogTable.status] == "SUCCESS" }.toLong(),
|
||||
failed = rows.count { it[SysApiAccessLogTable.status] != "SUCCESS" }.toLong(),
|
||||
avgCostMs = rows.map { it[SysApiAccessLogTable.costMs] }.average().toLong(),
|
||||
lastCalledAt = rows.maxByOrNull { it[SysApiAccessLogTable.createdAt] }
|
||||
?.get(SysApiAccessLogTable.createdAt)
|
||||
?.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResultRow.toEnterpriseResponse() = EnterpriseManageResponse(
|
||||
id = this[PtEnterpriseTable.id].toString(),
|
||||
taxpayerNum = this[PtEnterpriseTable.taxpayerNum],
|
||||
enterpriseName = this[PtEnterpriseTable.enterpriseName],
|
||||
legalPersonName = this[PtEnterpriseTable.legalPersonName],
|
||||
contactsName = this[PtEnterpriseTable.contactsName],
|
||||
contactsEmail = this[PtEnterpriseTable.contactsEmail],
|
||||
contactsPhone = this[PtEnterpriseTable.contactsPhone],
|
||||
regionCode = this[PtEnterpriseTable.regionCode],
|
||||
cityName = this[PtEnterpriseTable.cityName],
|
||||
enterpriseAddress = this[PtEnterpriseTable.enterpriseAddress],
|
||||
taxRegistrationCertificate = this[PtEnterpriseTable.taxRegistrationCertificate],
|
||||
invitationCode = this[PtEnterpriseTable.invitationCode],
|
||||
reviewStatus = this[PtEnterpriseTable.reviewStatus],
|
||||
reviewOpinion = this[PtEnterpriseTable.reviewOpinion],
|
||||
invoiceKind = this[PtEnterpriseTable.invoiceKind],
|
||||
invoiceLayoutFileType = this[PtEnterpriseTable.invoiceLayoutFileType],
|
||||
serviceStatus = this[PtEnterpriseTable.serviceStatus],
|
||||
bankName = this[PtEnterpriseTable.bankName],
|
||||
bankAccount = this[PtEnterpriseTable.bankAccount],
|
||||
presetAddress = this[PtEnterpriseTable.presetAddress],
|
||||
presetPhone = this[PtEnterpriseTable.presetPhone],
|
||||
)
|
||||
|
||||
fun ResultRow.toDigitalAccountItem() = DigitalAccountManageItem(
|
||||
id = this[PtDigitalAccountTable.id].toString(),
|
||||
enterpriseId = this[PtDigitalAccountTable.enterpriseId].toString(),
|
||||
taxpayerNum = this[PtDigitalAccountTable.taxpayerNum],
|
||||
account = this[PtDigitalAccountTable.account],
|
||||
name = this[PtDigitalAccountTable.name],
|
||||
identityType = this[PtDigitalAccountTable.identityType],
|
||||
operationProposed = this[PtDigitalAccountTable.operationProposed],
|
||||
authStatus = this[PtDigitalAccountTable.authStatus],
|
||||
switchable = this[PtDigitalAccountTable.switchable],
|
||||
wechatUserBindStatus = this[PtDigitalAccountTable.wechatUserBindStatus],
|
||||
lastAuthSuccTime = this[PtDigitalAccountTable.lastAuthSuccTime],
|
||||
loginAuthStatus = this[PtDigitalAccountTable.loginAuthStatus],
|
||||
lastLoginAuthTime = this[PtDigitalAccountTable.lastLoginAuthTime],
|
||||
riskAuthStatus = this[PtDigitalAccountTable.riskAuthStatus],
|
||||
lastRiskAuthTime = this[PtDigitalAccountTable.lastRiskAuthTime],
|
||||
platformUserId = this[PtDigitalAccountTable.platformUserId]?.toString(),
|
||||
platformUsername = this[PtDigitalAccountTable.platformUserId]?.let { platformUserId ->
|
||||
SysUserTable.selectAll()
|
||||
.where { (SysUserTable.id eq platformUserId) and SysUserTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
?.get(SysUserTable.username)
|
||||
},
|
||||
apiKey = this[PtDigitalAccountTable.apiKey],
|
||||
status = this[PtDigitalAccountTable.status],
|
||||
)
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
|
||||
package com.bbit.ticket.dao.piaotong
|
||||
|
||||
import com.bbit.ticket.database.piaotong.HistoryInvoiceOrderTable
|
||||
import com.bbit.ticket.database.piaotong.HistoryInvoiceVoucherTable
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.entity.common.PageResult
|
||||
import com.bbit.ticket.entity.request.AskInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.TaxRegisterInfo
|
||||
import com.bbit.ticket.entity.request.UpdateDigitalAccountRequest
|
||||
import com.bbit.ticket.entity.request.UpdateEnterpriseInfoRequest
|
||||
import com.bbit.ticket.entity.request.UpdatePresetDataRequest
|
||||
import com.bbit.ticket.entity.response.DigitalAccountResponse
|
||||
import com.bbit.ticket.entity.response.EnterpriseInfoResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailGoods
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailOrder
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailVoucher
|
||||
import com.bbit.ticket.entity.response.InvoiceHistoryItem
|
||||
import com.bbit.ticket.entity.response.PresetDataResponse
|
||||
import com.bbit.ticket.utils.formatDateTime
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.update
|
||||
import java.math.BigDecimal
|
||||
import java.time.LocalDateTime
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
|
||||
object EnterpriseTaxDao {
|
||||
|
||||
// =============================================
|
||||
// 企业信息
|
||||
// =============================================
|
||||
fun getEnterpriseInfo(userId: Uuid): EnterpriseInfoResponse? {
|
||||
val row = SysUserTable.selectAll().where { SysUserTable.id eq userId }.singleOrNull() ?: return null
|
||||
return EnterpriseInfoResponse(
|
||||
taxpayerNum = row[SysUserTable.taxpayerNum],
|
||||
enterpriseName = row[SysUserTable.taxEnterpriseName],
|
||||
legalPersonName = row[SysUserTable.taxLegalPersonName],
|
||||
contactsName = row[SysUserTable.taxContactName],
|
||||
contactsEmail = row[SysUserTable.taxContactEmail],
|
||||
contactsPhone = row[SysUserTable.taxContactPhone],
|
||||
regionCode = row[SysUserTable.taxRegionCode],
|
||||
cityName = row[SysUserTable.taxCityName],
|
||||
enterpriseAddress = row[SysUserTable.taxEnterpriseAddress],
|
||||
taxRegistrationCertificate = row[SysUserTable.taxRegistrationCertificate]
|
||||
)
|
||||
}
|
||||
|
||||
fun updateEnterpriseInfoLocal(userId: Uuid, req: UpdateEnterpriseInfoRequest) {
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.taxpayerNum] = req.taxpayerNum.trim().ifBlank { null }
|
||||
it[SysUserTable.taxEnterpriseName] = req.enterpriseName.trim().ifBlank { null }
|
||||
it[SysUserTable.taxLegalPersonName] = req.legalPersonName.trim().ifBlank { null }
|
||||
it[SysUserTable.taxContactName] = req.contactsName.trim().ifBlank { null }
|
||||
it[SysUserTable.taxContactEmail] = req.contactsEmail.trim().ifBlank { null }
|
||||
it[SysUserTable.taxContactPhone] = req.contactsPhone.trim().ifBlank { null }
|
||||
it[SysUserTable.taxRegionCode] = req.regionCode.trim().ifBlank { null }
|
||||
it[SysUserTable.taxCityName] = req.cityName.trim().ifBlank { null }
|
||||
it[SysUserTable.taxEnterpriseAddress] = req.enterpriseAddress.trim().ifBlank { null }
|
||||
it[SysUserTable.taxRegistrationCertificate] = req.taxRegistrationCertificate.ifBlank { null }
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 登记数电账号
|
||||
// =============================================
|
||||
|
||||
fun getDigitalAccount(userId: Uuid): DigitalAccountResponse? {
|
||||
val row = SysUserTable.selectAll().where { SysUserTable.id eq userId }.singleOrNull() ?: return null
|
||||
return DigitalAccountResponse(
|
||||
taxpayerNum = row[SysUserTable.taxpayerNum],
|
||||
taxAccount = row[SysUserTable.taxAccount]
|
||||
)
|
||||
}
|
||||
|
||||
fun updateDigitalAccountLocal(userId: Uuid, req: UpdateDigitalAccountRequest) {
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.taxpayerNum] = req.taxpayerNum.trim().ifBlank { null }
|
||||
it[SysUserTable.taxAccount] = req.taxAccount.trim().ifBlank { null }
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 开票预设数据
|
||||
// =============================================
|
||||
|
||||
fun getPresetData(userId: Uuid): PresetDataResponse? {
|
||||
val row = SysUserTable.selectAll().where { SysUserTable.id eq userId }.singleOrNull() ?: return null
|
||||
return PresetDataResponse(
|
||||
bankName = row[SysUserTable.bankName],
|
||||
bankAccount = row[SysUserTable.bankAccount],
|
||||
address = row[SysUserTable.presetAddress],
|
||||
phone = row[SysUserTable.presetPhone]
|
||||
)
|
||||
}
|
||||
|
||||
fun updatePresetData(userId: Uuid, req: UpdatePresetDataRequest) {
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.bankName] = req.bankName.trim().ifBlank { null }
|
||||
it[SysUserTable.bankAccount] = req.bankAccount.trim().ifBlank { null }
|
||||
it[SysUserTable.presetAddress] = req.address.trim().ifBlank { null }
|
||||
it[SysUserTable.presetPhone] = req.phone.trim().ifBlank { null }
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateEnterpriseInfo(userId: Uuid, req: TaxRegisterInfo) {
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.taxpayerNum] = req.taxpayerNum
|
||||
it[SysUserTable.taxEnterpriseName] = req.enterpriseName
|
||||
it[SysUserTable.taxContactName] = req.contactsName
|
||||
it[SysUserTable.taxContactPhone] = req.contactsPhone
|
||||
it[SysUserTable.taxContactEmail] = req.contactsEmail
|
||||
it[SysUserTable.taxLegalPersonName] = req.legalPersonName
|
||||
it[SysUserTable.taxRegionCode] = req.regionCode
|
||||
it[SysUserTable.taxCityName] = req.cityName
|
||||
it[SysUserTable.taxEnterpriseAddress] = req.enterpriseAddress
|
||||
it[SysUserTable.taxRegistrationCertificate] = req.taxRegistrationCertificate
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateUserAccount(userId: Uuid, taxpayerNum: String, taxAccount: String) {
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[SysUserTable.taxpayerNum] = taxpayerNum
|
||||
it[SysUserTable.taxAccount] = taxAccount
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,8 @@ object RedInvoiceDao {
|
||||
// 2. 插入红票历史记录(用于开票历史列表展示)
|
||||
HistoryInvoiceBasicTable.insert {
|
||||
it[HistoryInvoiceBasicTable.userId] = userId
|
||||
it[HistoryInvoiceBasicTable.enterpriseId] = blueRow?.get(HistoryInvoiceBasicTable.enterpriseId)
|
||||
it[HistoryInvoiceBasicTable.digitalAccountId] = blueRow?.get(HistoryInvoiceBasicTable.digitalAccountId)
|
||||
it[HistoryInvoiceBasicTable.invoiceReqSerialNo] = req.invoiceReqSerialNo
|
||||
|
||||
// ---- 状态 ----
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import java.time.OffsetDateTime
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object LogDao {
|
||||
private const val MAX_API_LOG_BODY_LENGTH = 20_000
|
||||
@@ -132,6 +133,9 @@ object LogDao {
|
||||
call: ApplicationCall,
|
||||
appKey: String?,
|
||||
appName: String?,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
interfaceCode: String?,
|
||||
requestBody: String?,
|
||||
responseCode: String?,
|
||||
responseBody: String?,
|
||||
@@ -143,6 +147,10 @@ object LogDao {
|
||||
it[traceId] = call.traceIdOrNull()
|
||||
it[SysApiAccessLogTable.appKey] = appKey?.take(100)
|
||||
it[SysApiAccessLogTable.appName] = appName?.take(100)
|
||||
it[SysApiAccessLogTable.enterpriseId] = enterpriseId
|
||||
it[SysApiAccessLogTable.digitalAccountId] = digitalAccountId
|
||||
it[SysApiAccessLogTable.apiKey] = appKey?.take(128)
|
||||
it[SysApiAccessLogTable.interfaceCode] = interfaceCode?.take(100)
|
||||
it[httpMethod] = call.request.httpMethod.value
|
||||
it[requestPath] = call.request.path().take(255)
|
||||
it[requestHeaders] = null
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.bbit.ticket.entity.common.ErrorCode
|
||||
import com.bbit.ticket.entity.common.PageResult
|
||||
import com.bbit.ticket.entity.common.statusLabel
|
||||
import com.bbit.ticket.entity.common.system.UserRoleBrief
|
||||
import com.bbit.ticket.utils.ApiKeyUtil
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import org.jetbrains.exposed.v1.core.Op
|
||||
import org.jetbrains.exposed.v1.core.ResultRow
|
||||
@@ -82,13 +81,39 @@ object UserDao {
|
||||
it[SysUserTable.avatar] = request.avatar.trimToNull()
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.status] = request.status
|
||||
it[SysUserTable.apiKey] = ApiKeyUtil.generate()
|
||||
it[SysUserTable.tokenVersion] = 1
|
||||
it[SysUserTable.createdAt] = now
|
||||
}
|
||||
return row[SysUserTable.id].toString()
|
||||
}
|
||||
|
||||
fun createPlatformUser(
|
||||
username: String,
|
||||
passwordHash: String,
|
||||
nickname: String?,
|
||||
realName: String?,
|
||||
phone: String?,
|
||||
enterpriseId: Uuid?,
|
||||
digitalAccountId: Uuid?,
|
||||
userType: String,
|
||||
): Uuid {
|
||||
val now = OffsetDateTime.now()
|
||||
val row = SysUserTable.insert {
|
||||
it[SysUserTable.username] = username.trim()
|
||||
it[SysUserTable.passwordHash] = passwordHash
|
||||
it[SysUserTable.nickname] = nickname?.trimToNull()
|
||||
it[SysUserTable.realName] = realName?.trimToNull()
|
||||
it[SysUserTable.phone] = phone?.trimToNull()
|
||||
it[SysUserTable.enterpriseId] = enterpriseId
|
||||
it[SysUserTable.digitalAccountId] = digitalAccountId
|
||||
it[SysUserTable.userType] = userType
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
it[SysUserTable.tokenVersion] = 1
|
||||
it[SysUserTable.createdAt] = now
|
||||
}
|
||||
return row[SysUserTable.id]
|
||||
}
|
||||
|
||||
fun detail(id: Uuid): UserDetailResponse {
|
||||
val user = requireActive(id)
|
||||
val roles = (SysUserRoleTable innerJoin SysRoleTable)
|
||||
@@ -121,10 +146,6 @@ object UserDao {
|
||||
it[SysUserTable.email] = request.email.trimToNull()
|
||||
it[SysUserTable.avatar] = request.avatar.trimToNull()
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.taxpayerNum] = request.taxpayerNum?.trimToNull()
|
||||
it[SysUserTable.taxAccount] = request.account?.trimToNull()
|
||||
it[SysUserTable.taxPassword] = request.taxPassword?.trimToNull()
|
||||
it[SysUserTable.taxIdentityType] = request.taxIdentityType?.trimToNull()
|
||||
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
@@ -168,6 +189,13 @@ object UserDao {
|
||||
SysRoleTable.deletedAt.isNull()
|
||||
}.count()
|
||||
|
||||
fun findEnabledRoleIdByCode(code: String): Uuid? =
|
||||
SysRoleTable.selectAll().where {
|
||||
(SysRoleTable.code eq code) and
|
||||
(SysRoleTable.status eq "ENABLED") and
|
||||
SysRoleTable.deletedAt.isNull()
|
||||
}.singleOrNull()?.get(SysRoleTable.id)
|
||||
|
||||
fun orgExists(orgId: Uuid): Boolean =
|
||||
SysOrgTable.selectAll().where {
|
||||
(SysOrgTable.id eq orgId) and SysOrgTable.deletedAt.isNull()
|
||||
@@ -240,7 +268,6 @@ object UserDao {
|
||||
status = this[SysUserTable.status],
|
||||
statusLabel = statusLabel(this[SysUserTable.status]),
|
||||
roleCodes = roleCodes,
|
||||
apiKey = this[SysUserTable.apiKey],
|
||||
)
|
||||
|
||||
private fun ResultRow.toUserDetail(
|
||||
@@ -262,7 +289,6 @@ object UserDao {
|
||||
statusLabel = statusLabel(this[SysUserTable.status]),
|
||||
roleIds = roleIds,
|
||||
roles = roles,
|
||||
apiKey = this[SysUserTable.apiKey],
|
||||
tokenVersion = this[SysUserTable.tokenVersion],
|
||||
lastLoginAt = this[SysUserTable.lastLoginAt]?.toString(),
|
||||
lastLoginIp = this[SysUserTable.lastLoginIp],
|
||||
@@ -273,21 +299,5 @@ object UserDao {
|
||||
deletedAt = this[SysUserTable.deletedAt]?.toString(),
|
||||
deletedBy = this[SysUserTable.deletedBy]?.toString(),
|
||||
version = this[SysUserTable.version],
|
||||
taxpayerNum = this[SysUserTable.taxpayerNum],
|
||||
account = this[SysUserTable.taxAccount],
|
||||
taxIdentityType = this[SysUserTable.taxIdentityType],
|
||||
taxContactName = this[SysUserTable.taxContactName],
|
||||
taxContactPhone = this[SysUserTable.taxContactPhone],
|
||||
taxContactEmail = this[SysUserTable.taxContactEmail],
|
||||
taxLegalPersonName = this[SysUserTable.taxLegalPersonName],
|
||||
taxEnterpriseName = this[SysUserTable.taxEnterpriseName],
|
||||
taxRegionCode = this[SysUserTable.taxRegionCode],
|
||||
taxCityName = this[SysUserTable.taxCityName],
|
||||
taxEnterpriseAddress = this[SysUserTable.taxEnterpriseAddress],
|
||||
taxRegistrationCertificate = this[SysUserTable.taxRegistrationCertificate],
|
||||
bankName = this[SysUserTable.bankName],
|
||||
bankAccount = this[SysUserTable.bankAccount],
|
||||
presetAddress = this[SysUserTable.presetAddress],
|
||||
presetPhone = this[SysUserTable.presetPhone],
|
||||
)
|
||||
}
|
||||
|
||||
+7
-1
@@ -25,6 +25,12 @@ object HistoryInvoiceBasicTable : Table("history_invoice_basic") {
|
||||
val userId = uuid("user_id")
|
||||
.nullable()
|
||||
|
||||
val enterpriseId = uuid("enterprise_id")
|
||||
.nullable()
|
||||
|
||||
val digitalAccountId = uuid("digital_account_id")
|
||||
.nullable()
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 发票请求信息
|
||||
// ----------------------------------------------------------------
|
||||
@@ -446,4 +452,4 @@ object HistoryInvoiceBasicTable : Table("history_invoice_basic") {
|
||||
val updatedAt = timestampWithTimeZone("updated_at").nullable()
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import kotlin.uuid.Uuid
|
||||
object OpenInvoiceBatchTable : Table("open_invoice_batch") {
|
||||
val id = uuid("id").clientDefault { Uuid.random() }
|
||||
val userId = uuid("user_id")
|
||||
val enterpriseId = uuid("enterprise_id").nullable()
|
||||
val digitalAccountId = uuid("digital_account_id").nullable()
|
||||
val batchNo = varchar("batch_no", 64)
|
||||
val totalCount = integer("total_count").default(0)
|
||||
val successCount = integer("success_count").default(0)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.bbit.ticket.database.piaotong
|
||||
|
||||
import org.jetbrains.exposed.v1.core.Table
|
||||
import org.jetbrains.exposed.v1.javatime.timestampWithTimeZone
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
object PtDigitalAccountTable : Table("pt_digital_account") {
|
||||
val id = uuid("id").clientDefault { Uuid.random() }
|
||||
val enterpriseId = uuid("enterprise_id").references(PtEnterpriseTable.id)
|
||||
val taxpayerNum = varchar("taxpayer_num", 50)
|
||||
val account = varchar("account", 100)
|
||||
val name = varchar("name", 50).nullable()
|
||||
val identityType = varchar("identity_type", 20).nullable()
|
||||
val operationProposed = varchar("operation_proposed", 20).nullable()
|
||||
val authStatus = varchar("auth_status", 20).nullable()
|
||||
val switchable = varchar("switchable", 20).nullable()
|
||||
val wechatUserBindStatus = varchar("wechat_user_bind_status", 20).nullable()
|
||||
val lastAuthSuccTime = varchar("last_auth_succ_time", 32).nullable()
|
||||
val loginAuthStatus = varchar("login_auth_status", 20).nullable()
|
||||
val lastLoginAuthTime = varchar("last_login_auth_time", 32).nullable()
|
||||
val riskAuthStatus = varchar("risk_auth_status", 20).nullable()
|
||||
val lastRiskAuthTime = varchar("last_risk_auth_time", 32).nullable()
|
||||
|
||||
val platformUserId = uuid("platform_user_id").nullable()
|
||||
val apiKey = varchar("api_key", 128).nullable().uniqueIndex()
|
||||
val status = varchar("status", 20).default("ENABLED")
|
||||
val createdAt = timestampWithTimeZone("created_at")
|
||||
val createdBy = uuid("created_by").nullable()
|
||||
val updatedAt = timestampWithTimeZone("updated_at").nullable()
|
||||
val updatedBy = uuid("updated_by").nullable()
|
||||
val deletedAt = timestampWithTimeZone("deleted_at").nullable()
|
||||
val deletedBy = uuid("deleted_by").nullable()
|
||||
val version = integer("version").default(1)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
|
||||
init {
|
||||
uniqueIndex(enterpriseId, account)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.bbit.ticket.database.piaotong
|
||||
|
||||
import org.jetbrains.exposed.v1.core.Table
|
||||
import org.jetbrains.exposed.v1.javatime.timestampWithTimeZone
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
object PtEnterpriseTable : Table("pt_enterprise") {
|
||||
val id = uuid("id").clientDefault { Uuid.random() }
|
||||
val taxpayerNum = varchar("taxpayer_num", 50).uniqueIndex()
|
||||
val enterpriseName = varchar("enterprise_name", 200)
|
||||
val legalPersonName = varchar("legal_person_name", 50).nullable()
|
||||
val contactsName = varchar("contacts_name", 50).nullable()
|
||||
val contactsEmail = varchar("contacts_email", 100).nullable()
|
||||
val contactsPhone = varchar("contacts_phone", 32).nullable()
|
||||
val regionCode = varchar("region_code", 32).nullable()
|
||||
val cityName = varchar("city_name", 50).nullable()
|
||||
val enterpriseAddress = varchar("enterprise_address", 200).nullable()
|
||||
val taxRegistrationCertificate = text("tax_registration_certificate").nullable()
|
||||
|
||||
val invitationCode = varchar("invitation_code", 20).nullable()
|
||||
val reviewStatus = varchar("review_status", 20).nullable()
|
||||
val reviewOpinion = varchar("review_opinion", 200).nullable()
|
||||
val invoiceKind = varchar("invoice_kind", 100).nullable()
|
||||
val invoiceLayoutFileType = varchar("invoice_layout_file_type", 20).nullable()
|
||||
val serviceStatus = varchar("service_status", 20).nullable()
|
||||
|
||||
val bankName = varchar("bank_name", 100).nullable()
|
||||
val bankAccount = varchar("bank_account", 50).nullable()
|
||||
val presetAddress = varchar("preset_address", 200).nullable()
|
||||
val presetPhone = varchar("preset_phone", 32).nullable()
|
||||
|
||||
val createdAt = timestampWithTimeZone("created_at")
|
||||
val createdBy = uuid("created_by").nullable()
|
||||
val updatedAt = timestampWithTimeZone("updated_at").nullable()
|
||||
val updatedBy = uuid("updated_by").nullable()
|
||||
val deletedAt = timestampWithTimeZone("deleted_at").nullable()
|
||||
val deletedBy = uuid("deleted_by").nullable()
|
||||
val version = integer("version").default(1)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
@@ -10,6 +10,10 @@ object SysApiAccessLogTable : Table("sys_api_access_log") {
|
||||
val traceId = varchar("trace_id", 64).nullable()
|
||||
val appKey = varchar("app_key", 100).nullable()
|
||||
val appName = varchar("app_name", 100).nullable()
|
||||
val enterpriseId = uuid("enterprise_id").nullable()
|
||||
val digitalAccountId = uuid("digital_account_id").nullable()
|
||||
val apiKey = varchar("api_key", 128).nullable()
|
||||
val interfaceCode = varchar("interface_code", 100).nullable()
|
||||
val httpMethod = varchar("http_method", 20)
|
||||
val requestPath = varchar("request_path", 255)
|
||||
val requestHeaders = text("request_headers").nullable()
|
||||
|
||||
@@ -15,7 +15,9 @@ object SysUserTable : Table("sys_user") {
|
||||
val avatar = text("avatar").nullable()
|
||||
val orgId = uuid("org_id").nullable()
|
||||
val status = varchar("status", 20).default("ENABLED")
|
||||
val apiKey = varchar("api_key", 128).nullable().uniqueIndex()
|
||||
val enterpriseId = uuid("enterprise_id").nullable()
|
||||
val digitalAccountId = uuid("digital_account_id").nullable()
|
||||
val userType = varchar("user_type", 30).default("SYSTEM")
|
||||
val tokenVersion = integer("token_version").default(1)
|
||||
val lastLoginAt = timestampWithTimeZone("last_login_at").nullable()
|
||||
val lastLoginIp = varchar("last_login_ip", 64).nullable()
|
||||
@@ -29,24 +31,6 @@ object SysUserTable : Table("sys_user") {
|
||||
|
||||
val realName = varchar("real_name", 50).nullable()
|
||||
val phone = varchar("phone", 32).nullable()
|
||||
val taxpayerNum = varchar("tax_payer_num", 50).nullable()
|
||||
val taxAccount = varchar("tax_account", 50).nullable()
|
||||
val taxPassword = varchar("tax_password", 50).nullable()
|
||||
val taxIdentityType = varchar("tax_identity_type", 50).nullable()
|
||||
val taxContactName = varchar("tax_contact_name", 50).nullable()
|
||||
val taxContactPhone = varchar("tax_contact_phone", 32).nullable()
|
||||
val taxContactEmail = varchar("tax_contact_email", 100).nullable()
|
||||
val taxLegalPersonName = varchar("tax_legal_person_name", 50).nullable()
|
||||
val taxEnterpriseName = varchar("tax_enterprise_name", 200).nullable()
|
||||
val taxRegionCode = varchar("tax_region_code", 32).nullable()
|
||||
val taxCityName = varchar("tax_city_name", 50).nullable()
|
||||
val taxEnterpriseAddress = varchar("tax_enterprise_address", 200).nullable()
|
||||
val taxRegistrationCertificate = text("tax_registration_certificate").nullable()
|
||||
|
||||
val bankName = varchar("bank_name", 100).nullable()
|
||||
val bankAccount = varchar("bank_account", 50).nullable()
|
||||
val presetAddress = varchar("preset_address", 200).nullable()
|
||||
val presetPhone = varchar("preset_phone", 32).nullable()
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
@@ -34,12 +34,11 @@ data class CurrentUserProfile(
|
||||
val phone: String? = null,
|
||||
val email: String? = null,
|
||||
val orgId: String? = null,
|
||||
val enterpriseId: String? = null,
|
||||
val digitalAccountId: String? = null,
|
||||
val userType: String = "SYSTEM",
|
||||
val status: String,
|
||||
val createdAt: String? = null,
|
||||
val taxpayerNum: String? = null,
|
||||
val account: String? = null,
|
||||
val taxPassword: String? = null,
|
||||
val taxIdentityType: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -11,7 +11,6 @@ data class UserListItem(
|
||||
val status: String,
|
||||
val statusLabel: String,
|
||||
val roleCodes: List<String>,
|
||||
val apiKey: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -30,7 +29,6 @@ data class UserDetailResponse(
|
||||
val statusLabel: String,
|
||||
val roleIds: List<String>,
|
||||
val roles: List<UserRoleBrief> = emptyList(),
|
||||
val apiKey: String? = null,
|
||||
val tokenVersion: Int,
|
||||
val lastLoginAt: String? = null,
|
||||
val lastLoginIp: String? = null,
|
||||
@@ -41,22 +39,6 @@ data class UserDetailResponse(
|
||||
val deletedAt: String? = null,
|
||||
val deletedBy: String? = null,
|
||||
val version: Int,
|
||||
val taxpayerNum: String? = null,
|
||||
val account: String? = null,
|
||||
val taxIdentityType: String? = null,
|
||||
val taxContactName: String? = null,
|
||||
val taxContactPhone: String? = null,
|
||||
val taxContactEmail: String? = null,
|
||||
val taxLegalPersonName: String? = null,
|
||||
val taxEnterpriseName: String? = null,
|
||||
val taxRegionCode: String? = null,
|
||||
val taxCityName: String? = null,
|
||||
val taxEnterpriseAddress: String? = null,
|
||||
val taxRegistrationCertificate: String? = null,
|
||||
val bankName: String? = null,
|
||||
val bankAccount: String? = null,
|
||||
val presetAddress: String? = null,
|
||||
val presetPhone: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -87,10 +69,6 @@ data class UpdateUserRequest(
|
||||
val email: String? = null,
|
||||
val avatar: String? = null,
|
||||
val orgId: String? = null,
|
||||
val taxpayerNum: String? = null,
|
||||
val account: String? = null,
|
||||
val taxPassword: String? = null,
|
||||
val taxIdentityType: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -8,6 +8,11 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class AskInvoiceRequest(
|
||||
|
||||
/**
|
||||
* 平台数电账号 ID。企业管理员开票时用于指定开票员。
|
||||
*/
|
||||
val digitalAccountId: String? = null,
|
||||
|
||||
/**
|
||||
* 销方纳税人识别号(销售方税号)
|
||||
* 长度15~20,只允许大写字母和数字
|
||||
@@ -448,4 +453,4 @@ data class OrderInfo(
|
||||
* 业务单据号
|
||||
*/
|
||||
val orderNo: String
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EnterpriseRegisterRequest(
|
||||
val taxpayerNum: String,
|
||||
val enterpriseName: String,
|
||||
val legalPersonName: String? = null,
|
||||
val contactsName: String? = null,
|
||||
val contactsEmail: String? = null,
|
||||
val contactsPhone: String,
|
||||
val regionCode: String,
|
||||
val cityName: String = "",
|
||||
val enterpriseAddress: String? = null,
|
||||
val taxRegistrationCertificate: String? = null,
|
||||
val password: String,
|
||||
val confirmPassword: String,
|
||||
)
|
||||
|
||||
fun EnterpriseRegisterRequest.toTaxRegisterInfo(): TaxRegisterInfo =
|
||||
TaxRegisterInfo(
|
||||
taxpayerNum = taxpayerNum.trim(),
|
||||
enterpriseName = enterpriseName.trim(),
|
||||
legalPersonName = legalPersonName?.trim()?.ifBlank { null },
|
||||
contactsName = contactsName?.trim()?.ifBlank { null },
|
||||
contactsEmail = contactsEmail?.trim()?.ifBlank { null },
|
||||
contactsPhone = contactsPhone.trim(),
|
||||
regionCode = regionCode.trim(),
|
||||
cityName = cityName.trim(),
|
||||
enterpriseAddress = enterpriseAddress?.trim()?.ifBlank { null },
|
||||
taxRegistrationCertificate = taxRegistrationCertificate?.trim()?.ifBlank { null },
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class CreateDigitalAccountRequest(
|
||||
val account: String,
|
||||
val taxPassword: String,
|
||||
val identityType: String,
|
||||
val phoneNum: String,
|
||||
val name: String,
|
||||
val platformPassword: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class UpdateInvoiceSettingRequest(
|
||||
val bankName: String = "",
|
||||
val bankAccount: String = "",
|
||||
val address: String = "",
|
||||
val phone: String = "",
|
||||
)
|
||||
@@ -11,16 +11,16 @@ data class TaxRegisterInfo(
|
||||
val enterpriseName: String,
|
||||
|
||||
/** 法人姓名 */
|
||||
val legalPersonName: String,
|
||||
val legalPersonName: String?,
|
||||
|
||||
/** 联系人姓名 */
|
||||
val contactsName: String,
|
||||
val contactsName: String?,
|
||||
|
||||
/** 联系人邮箱 */
|
||||
val contactsEmail: String,
|
||||
val contactsEmail: String?,
|
||||
|
||||
/** 联系人手机号 */
|
||||
val contactsPhone: String,
|
||||
val contactsPhone: String?,
|
||||
|
||||
/** 区域编码(行政区划代码,如省/市级编码) */
|
||||
val regionCode: String,
|
||||
@@ -29,8 +29,8 @@ data class TaxRegisterInfo(
|
||||
val cityName: String,
|
||||
|
||||
/** 企业详细地址 */
|
||||
val enterpriseAddress: String,
|
||||
val enterpriseAddress: String?,
|
||||
|
||||
/** 税务登记证编号 / 税务登记证明标识 */
|
||||
val taxRegistrationCertificate: String
|
||||
val taxRegistrationCertificate: String?
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TaxRegisterUserRequest(
|
||||
val taxpayerNum: String,
|
||||
val taxAccount: String
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdateDigitalAccountRequest(
|
||||
/** 纳税人识别号 / 税号 */
|
||||
val taxpayerNum: String = "",
|
||||
/** 电子税局账号 */
|
||||
val taxAccount: String = ""
|
||||
)
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdateEnterpriseInfoRequest(
|
||||
/** 纳税人识别号 / 税号 */
|
||||
val taxpayerNum: String = "",
|
||||
/** 企业名称 */
|
||||
val enterpriseName: String = "",
|
||||
/** 法人姓名 */
|
||||
val legalPersonName: String = "",
|
||||
/** 联系人姓名 */
|
||||
val contactsName: String = "",
|
||||
/** 联系人邮箱 */
|
||||
val contactsEmail: String = "",
|
||||
/** 联系人手机号 */
|
||||
val contactsPhone: String = "",
|
||||
/** 区域编码(行政区划代码,如省/市级编码) */
|
||||
val regionCode: String = "",
|
||||
/** 城市/区县名称 */
|
||||
val cityName: String = "",
|
||||
/** 企业详细地址 */
|
||||
val enterpriseAddress: String = "",
|
||||
/** 税务登记证图片 base64 */
|
||||
val taxRegistrationCertificate: String = ""
|
||||
)
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdatePresetDataRequest(
|
||||
val bankName: String = "",
|
||||
val bankAccount: String = "",
|
||||
val address: String = "",
|
||||
val phone: String = ""
|
||||
)
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EnterpriseInfoResponse(
|
||||
val taxpayerNum: String?,
|
||||
val enterpriseName: String?,
|
||||
val legalPersonName: String?,
|
||||
val contactsName: String?,
|
||||
val contactsEmail: String?,
|
||||
val contactsPhone: String?,
|
||||
val regionCode: String?,
|
||||
val cityName: String?,
|
||||
val enterpriseAddress: String?,
|
||||
val taxRegistrationCertificate: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DigitalAccountResponse(
|
||||
val taxpayerNum: String?,
|
||||
val taxAccount: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PresetDataResponse(
|
||||
val bankName: String?,
|
||||
val bankAccount: String?,
|
||||
val address: String?,
|
||||
val phone: String?
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EnterpriseManageResponse(
|
||||
val id: String,
|
||||
val taxpayerNum: String,
|
||||
val enterpriseName: String,
|
||||
val legalPersonName: String? = null,
|
||||
val contactsName: String? = null,
|
||||
val contactsEmail: String? = null,
|
||||
val contactsPhone: String? = null,
|
||||
val regionCode: String? = null,
|
||||
val cityName: String? = null,
|
||||
val enterpriseAddress: String? = null,
|
||||
val taxRegistrationCertificate: String? = null,
|
||||
val invitationCode: String? = null,
|
||||
val reviewStatus: String? = null,
|
||||
val reviewOpinion: String? = null,
|
||||
val invoiceKind: String? = null,
|
||||
val invoiceLayoutFileType: String? = null,
|
||||
val serviceStatus: String? = null,
|
||||
val bankName: String? = null,
|
||||
val bankAccount: String? = null,
|
||||
val presetAddress: String? = null,
|
||||
val presetPhone: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DigitalAccountManageItem(
|
||||
val id: String,
|
||||
val enterpriseId: String,
|
||||
val taxpayerNum: String,
|
||||
val account: String,
|
||||
val name: String? = null,
|
||||
val identityType: String? = null,
|
||||
val operationProposed: String? = null,
|
||||
val authStatus: String? = null,
|
||||
val switchable: String? = null,
|
||||
val wechatUserBindStatus: String? = null,
|
||||
val lastAuthSuccTime: String? = null,
|
||||
val loginAuthStatus: String? = null,
|
||||
val lastLoginAuthTime: String? = null,
|
||||
val riskAuthStatus: String? = null,
|
||||
val lastRiskAuthTime: String? = null,
|
||||
val platformUserId: String? = null,
|
||||
val platformUsername: String? = null,
|
||||
val apiKey: String? = null,
|
||||
val status: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class OpenApiStatisticsItem(
|
||||
val digitalAccountId: String? = null,
|
||||
val account: String? = null,
|
||||
val interfaceCode: String? = null,
|
||||
val total: Long,
|
||||
val success: Long,
|
||||
val failed: Long,
|
||||
val avgCostMs: Long,
|
||||
val lastCalledAt: String? = null,
|
||||
)
|
||||
+1
-126
@@ -2,144 +2,19 @@ package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* 查询数电账号列表响应
|
||||
*/
|
||||
@Serializable
|
||||
data class QueryDigitalAccountListResponse(
|
||||
|
||||
/**
|
||||
* 数电账号列表
|
||||
*/
|
||||
val list: List<DigitalAccountInfo>
|
||||
)
|
||||
|
||||
/**
|
||||
* 数电账号信息
|
||||
*/
|
||||
@Serializable
|
||||
data class DigitalAccountInfo(
|
||||
|
||||
/**
|
||||
* 销售方纳税人识别号
|
||||
*
|
||||
* 长度:15-20
|
||||
* 只能包含大写英文字母或数字
|
||||
*/
|
||||
val taxpayerNum: String,
|
||||
|
||||
/**
|
||||
* 电子税局登录账号
|
||||
*/
|
||||
val account: String,
|
||||
|
||||
/**
|
||||
* 人员姓名
|
||||
*/
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* 登录身份类型
|
||||
*
|
||||
* 01:法定代表人
|
||||
* 02:财务负责人
|
||||
* 03:办税员
|
||||
* 04:涉税服务人员
|
||||
* 05:管理员
|
||||
* 07:领票人
|
||||
* 09:开票员
|
||||
* 99:其他人员
|
||||
*/
|
||||
val identityType: String,
|
||||
|
||||
/**
|
||||
* 操作建议
|
||||
*
|
||||
* 注意:
|
||||
* 该值是根据账号状态及不同地区登录方式综合判断,
|
||||
* 并非与账号状态一一对应。
|
||||
*
|
||||
* 0:无需认证
|
||||
* 1:需扫码认证
|
||||
* 2:需扫码或短信认证
|
||||
* 3:需短信认证
|
||||
*/
|
||||
val operationProposed: String,
|
||||
|
||||
/**
|
||||
* 账号状态
|
||||
*
|
||||
* 0:无需认证
|
||||
* 1:风险认证
|
||||
* 2:登录认证
|
||||
* 3:风险 + 登录认证
|
||||
*
|
||||
* 注:
|
||||
* 风险认证通常需要在开票时才能得知。
|
||||
*/
|
||||
val authStatus: String,
|
||||
|
||||
/**
|
||||
* 是否可切换企业状态
|
||||
*
|
||||
* 0:不可切换
|
||||
* 1:可切换
|
||||
*
|
||||
* 表示该账号在当前地区其他企业已存在登录状态,
|
||||
* 当前企业无需重复登录认证。
|
||||
* 若发生开票,将自动调度切换至当前企业。
|
||||
*/
|
||||
val switchable: String,
|
||||
|
||||
/**
|
||||
* 是否绑定微信公众号
|
||||
*
|
||||
* 是否绑定票通云服务微信公众号。
|
||||
*
|
||||
* 0:否
|
||||
* 1:是
|
||||
*/
|
||||
val wechatUserBindStatus: String,
|
||||
|
||||
/**
|
||||
* 最新认证成功时间
|
||||
*
|
||||
* 包括登录认证或风险认证成功时间。
|
||||
*
|
||||
* 时间格式:
|
||||
* yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
val lastAuthSuccTime: String? = null,
|
||||
|
||||
/**
|
||||
* 登录认证状态
|
||||
*
|
||||
* 0:未登录
|
||||
* 1:已登录
|
||||
*/
|
||||
val loginAuthStatus: String,
|
||||
|
||||
/**
|
||||
* 最新登录认证时间
|
||||
*
|
||||
* 时间格式:
|
||||
* yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
val lastLoginAuthTime: String? = null,
|
||||
|
||||
/**
|
||||
* 风险认证状态
|
||||
*
|
||||
* 0:未认证
|
||||
* 1:已认证
|
||||
*/
|
||||
val riskAuthStatus: String,
|
||||
|
||||
/**
|
||||
* 最新风险认证时间
|
||||
*
|
||||
* 时间格式:
|
||||
* yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
val lastRiskAuthTime: String? = null
|
||||
)
|
||||
)
|
||||
|
||||
+19
-15
@@ -11,6 +11,7 @@ import com.bbit.ticket.entity.openapi.OpenBlueInvoiceCreateRequest
|
||||
import com.bbit.ticket.service.openapi.OpenBlueInvoiceService
|
||||
import com.bbit.ticket.service.system.ApiAccessLogService
|
||||
import com.bbit.ticket.utils.requireOpenApiPrincipal
|
||||
import com.bbit.ticket.utils.OpenApiPrincipal
|
||||
import com.bbit.ticket.utils.plugins.myJson
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
@@ -33,7 +34,7 @@ fun Route.registerOpenBlueInvoiceRoutes() {
|
||||
post {
|
||||
val principal = call.requireOpenApiPrincipal()
|
||||
val request = call.receive<OpenBlueInvoiceCreateRequest>()
|
||||
call.respondOpenApi(principal.apiKey, principal.username, myJson.encodeToString(request)) {
|
||||
call.respondOpenApi(principal, "blue-invoice.create", myJson.encodeToString(request)) {
|
||||
OpenBlueInvoiceService.createSingle(principal, request)
|
||||
}
|
||||
}
|
||||
@@ -41,7 +42,7 @@ fun Route.registerOpenBlueInvoiceRoutes() {
|
||||
get("/{invoiceReqSerialNo}") {
|
||||
val principal = call.requireOpenApiPrincipal()
|
||||
val invoiceReqSerialNo = call.parameters["invoiceReqSerialNo"].orEmpty()
|
||||
call.respondOpenApi(principal.apiKey, principal.username, null) {
|
||||
call.respondOpenApi(principal, "blue-invoice.query", null) {
|
||||
OpenBlueInvoiceService.querySingle(principal, invoiceReqSerialNo)
|
||||
}
|
||||
}
|
||||
@@ -49,7 +50,7 @@ fun Route.registerOpenBlueInvoiceRoutes() {
|
||||
get("/sample/{invoiceReqSerialNo}") {
|
||||
val principal = call.requireOpenApiPrincipal()
|
||||
val invoiceReqSerialNo = call.parameters["invoiceReqSerialNo"].orEmpty()
|
||||
call.respondOpenApi(principal.apiKey, principal.username, null) {
|
||||
call.respondOpenApi(principal, "blue-invoice.sample", null) {
|
||||
OpenBlueInvoiceService.sample(principal, invoiceReqSerialNo)
|
||||
}
|
||||
}
|
||||
@@ -57,7 +58,7 @@ fun Route.registerOpenBlueInvoiceRoutes() {
|
||||
post("/batches") {
|
||||
val principal = call.requireOpenApiPrincipal()
|
||||
val request = call.receive<OpenBlueInvoiceBatchCreateRequest>()
|
||||
call.respondOpenApi(principal.apiKey, principal.username, myJson.encodeToString(request)) {
|
||||
call.respondOpenApi(principal, "blue-invoice.batch-create", myJson.encodeToString(request)) {
|
||||
val response = OpenBlueInvoiceService.createBatch(principal, request)
|
||||
call.application.launch {
|
||||
OpenBlueInvoiceService.processBatch(principal, request.batchNo)
|
||||
@@ -69,7 +70,7 @@ fun Route.registerOpenBlueInvoiceRoutes() {
|
||||
get("/batches/{batchNo}") {
|
||||
val principal = call.requireOpenApiPrincipal()
|
||||
val batchNo = call.parameters["batchNo"].orEmpty()
|
||||
call.respondOpenApi(principal.apiKey, principal.username, null) {
|
||||
call.respondOpenApi(principal, "blue-invoice.batch-query", null) {
|
||||
OpenBlueInvoiceService.queryBatch(principal, batchNo)
|
||||
}
|
||||
}
|
||||
@@ -84,8 +85,8 @@ fun Route.registerOpenBlueInvoiceRoutes() {
|
||||
* @param block 当前接口要执行的业务逻辑。
|
||||
*/
|
||||
private suspend inline fun <reified T> ApplicationCall.respondOpenApi(
|
||||
appKey: String?,
|
||||
appName: String?,
|
||||
principal: OpenApiPrincipal,
|
||||
interfaceCode: String,
|
||||
requestBody: String?,
|
||||
crossinline block: suspend () -> T,
|
||||
) {
|
||||
@@ -94,22 +95,22 @@ private suspend inline fun <reified T> ApplicationCall.respondOpenApi(
|
||||
val response = ok(block())
|
||||
val responseBody = myJson.encodeToString(response)
|
||||
respondText(responseBody, ContentType.Application.Json)
|
||||
saveOpenApiLog(appKey, appName, requestBody, response.code, responseBody, "SUCCESS", null, start)
|
||||
saveOpenApiLog(principal, interfaceCode, requestBody, response.code, responseBody, "SUCCESS", null, start)
|
||||
} catch (e: PTException) {
|
||||
val response = fail(code = e.code, message = e.message, traceId = e.serialNo)
|
||||
val responseBody = myJson.encodeToString(response)
|
||||
respondText(responseBody, ContentType.Application.Json)
|
||||
saveOpenApiLog(appKey, appName, requestBody, e.code, responseBody, "FAILED", e.message, start)
|
||||
saveOpenApiLog(principal, interfaceCode, requestBody, e.code, responseBody, "FAILED", e.message, start)
|
||||
} catch (e: BizException) {
|
||||
val response = fail(code = e.errorCode, message = e.message)
|
||||
val responseBody = myJson.encodeToString(response)
|
||||
respondText(responseBody, ContentType.Application.Json, e.status)
|
||||
saveOpenApiLog(appKey, appName, requestBody, e.errorCode, responseBody, "FAILED", e.message, start)
|
||||
saveOpenApiLog(principal, interfaceCode, requestBody, e.errorCode, responseBody, "FAILED", e.message, start)
|
||||
} catch (e: Exception) {
|
||||
val response = fail(code = "-1", message = e.message ?: "开放接口调用失败")
|
||||
val responseBody = myJson.encodeToString(response)
|
||||
respondText(responseBody, ContentType.Application.Json)
|
||||
saveOpenApiLog(appKey, appName, requestBody, "-1", responseBody, "FAILED", e.message, start)
|
||||
saveOpenApiLog(principal, interfaceCode, requestBody, "-1", responseBody, "FAILED", e.message, start)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +127,8 @@ private suspend inline fun <reified T> ApplicationCall.respondOpenApi(
|
||||
* @param start 接口开始时间标记。
|
||||
*/
|
||||
private suspend fun ApplicationCall.saveOpenApiLog(
|
||||
appKey: String?,
|
||||
appName: String?,
|
||||
principal: OpenApiPrincipal,
|
||||
interfaceCode: String,
|
||||
requestBody: String?,
|
||||
responseCode: String?,
|
||||
responseBody: String?,
|
||||
@@ -137,8 +138,11 @@ private suspend fun ApplicationCall.saveOpenApiLog(
|
||||
) {
|
||||
ApiAccessLogService.save(
|
||||
call = this,
|
||||
appKey = appKey,
|
||||
appName = appName,
|
||||
appKey = principal.apiKey,
|
||||
appName = principal.username,
|
||||
enterpriseId = principal.enterpriseId,
|
||||
digitalAccountId = principal.digitalAccountId,
|
||||
interfaceCode = interfaceCode,
|
||||
requestBody = requestBody,
|
||||
responseCode = responseCode,
|
||||
responseBody = responseBody,
|
||||
|
||||
@@ -2,22 +2,16 @@
|
||||
|
||||
package com.bbit.ticket.route.piaotong
|
||||
|
||||
import com.bbit.ticket.entity.common.BizException
|
||||
import com.bbit.ticket.entity.request.AuthQrcodeRequest
|
||||
import com.bbit.ticket.entity.request.CreateDigitalAccountRequest
|
||||
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.TaxRegisterInfo
|
||||
import com.bbit.ticket.entity.request.TaxRegisterUserRequest
|
||||
import com.bbit.ticket.entity.request.UpdateDigitalAccountRequest
|
||||
import com.bbit.ticket.entity.request.UpdateEnterpriseInfoRequest
|
||||
import com.bbit.ticket.entity.request.UpdatePresetDataRequest
|
||||
import com.bbit.ticket.entity.request.UpdateInvoiceSettingRequest
|
||||
import com.bbit.ticket.service.piaotong.PTAuthService
|
||||
import com.bbit.ticket.service.piaotong.PTConfigService
|
||||
import com.bbit.ticket.utils.requireCurrentUser
|
||||
import com.bbit.ticket.utils.requirePtProfile
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
@@ -33,85 +27,80 @@ import kotlin.uuid.ExperimentalUuidApi
|
||||
fun Route.registerPTAuthRoutes() {
|
||||
get("/info") {
|
||||
call.respondPt("查询票通认证状态失败") {
|
||||
val profile = call.requireCurrentUser().requirePtProfile()
|
||||
PTAuthService.getTaxBureauAccountAuthStatus(
|
||||
TaxBureauAuthReq(profile.taxpayerNum, profile.taxAccount)
|
||||
val account = PTConfigService.requireDigitalAccountForAction(
|
||||
call.requireCurrentUser(),
|
||||
call.request.queryParameters["digitalAccountId"],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
post("/register") {
|
||||
call.respondPt("企业注册失败") {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
PTAuthService.registerEnterprise(call.receive<TaxRegisterInfo>(), currentUser.id)
|
||||
}
|
||||
}
|
||||
|
||||
post("/registerUser") {
|
||||
call.respondPt("用户注册失败") {
|
||||
PTAuthService.registerUserFromPayload(
|
||||
call.receive<TaxRegisterUserRequest>(),
|
||||
call.requireCurrentUser()
|
||||
PTAuthService.getTaxBureauAccountAuthStatus(
|
||||
TaxBureauAuthReq(account.taxpayerNum, account.account)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
get("/enterprise") {
|
||||
call.respondPtOrEmptyObject("查询企业信息失败") {
|
||||
PTConfigService.getEnterpriseInfo(call.requireCurrentUser().id)
|
||||
PTConfigService.getEnterpriseInfo(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
put("/enterprise") {
|
||||
call.respondPt("保存企业信息失败") {
|
||||
PTConfigService.updateEnterpriseInfo(
|
||||
call.requireCurrentUser().id,
|
||||
call.receive<UpdateEnterpriseInfoRequest>()
|
||||
)
|
||||
post("/enterprise/refresh") {
|
||||
call.respondPt("刷新企业信息失败") {
|
||||
PTConfigService.refreshEnterpriseInfo(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
get("/digital-account") {
|
||||
call.respondPtOrEmptyObject("查询数电账号失败") {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
if (currentUser.taxPayerNum == null) {
|
||||
throw BizException("-1", "请先完善用户信息", HttpStatusCode.OK)
|
||||
}
|
||||
PTConfigService.getDigitalAccount(currentUser.id)
|
||||
get("/enterprise/bank-accounts") {
|
||||
call.respondPt("查询企业开户行及账号失败") {
|
||||
PTConfigService.queryEnterpriseBankAccounts(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
put("/digital-account") {
|
||||
call.respondPt("保存数电账号失败") {
|
||||
PTConfigService.updateDigitalAccount(
|
||||
call.requireCurrentUser().id,
|
||||
call.receive<UpdateDigitalAccountRequest>()
|
||||
)
|
||||
get("/digital-accounts") {
|
||||
call.respondPt("查询数电账号失败") {
|
||||
PTConfigService.listDigitalAccounts(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
post("/digital-accounts/refresh") {
|
||||
call.respondPt("刷新数电账号失败") {
|
||||
PTConfigService.refreshDigitalAccounts(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
post("/digital-accounts") {
|
||||
call.respondPt("新增数电账号失败") {
|
||||
PTConfigService.createDigitalAccount(call.requireCurrentUser(), call.receive<CreateDigitalAccountRequest>())
|
||||
}
|
||||
}
|
||||
|
||||
get("/preset") {
|
||||
call.respondPtOrEmptyObject("查询预设数据失败") {
|
||||
PTConfigService.getPresetData(call.requireCurrentUser().id)
|
||||
PTConfigService.getEnterpriseInfo(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
put("/preset") {
|
||||
call.respondPt("保存预设数据失败") {
|
||||
PTConfigService.updatePresetData(
|
||||
call.requireCurrentUser().id,
|
||||
call.receive<UpdatePresetDataRequest>()
|
||||
)
|
||||
PTConfigService.updateInvoiceSetting(call.requireCurrentUser(), call.receive<UpdateInvoiceSettingRequest>())
|
||||
}
|
||||
}
|
||||
|
||||
get("/openapi/statistics") {
|
||||
call.respondPt("查询 OpenAPI 统计失败") {
|
||||
PTConfigService.openApiStatistics(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
get("/authentication") {
|
||||
call.respondPt("获取认证二维码失败") {
|
||||
val profile = call.requireCurrentUser().requirePtProfile()
|
||||
val account = PTConfigService.requireDigitalAccountForAction(
|
||||
call.requireCurrentUser(),
|
||||
call.request.queryParameters["digitalAccountId"],
|
||||
)
|
||||
PTAuthService.getAuthenticationQrcode(
|
||||
AuthQrcodeRequest(
|
||||
taxpayerNum = profile.taxpayerNum,
|
||||
account = profile.taxAccount,
|
||||
taxpayerNum = account.taxpayerNum,
|
||||
account = account.account,
|
||||
qrcodeType = call.request.queryParameters["qrcodeType"]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.bbit.ticket.entity.request.RedCreateRequest
|
||||
import com.bbit.ticket.service.piaotong.PTBlueService
|
||||
import com.bbit.ticket.service.piaotong.PTRedService
|
||||
import com.bbit.ticket.utils.requireCurrentUser
|
||||
import com.bbit.ticket.utils.requirePtProfile
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
@@ -38,7 +37,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
call.respondPt("查询开票历史失败") {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
PTBlueService.getInvoiceBlueHistory(
|
||||
userId = currentUser.id,
|
||||
user = currentUser,
|
||||
page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1,
|
||||
pageSize = call.request.queryParameters["pageSize"]?.toIntOrNull() ?: 20,
|
||||
invoiceType = call.request.queryParameters["invoiceType"],
|
||||
@@ -50,7 +49,7 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
|
||||
get("/invoiceBatchNos") {
|
||||
call.respondPt("查询批次号列表失败") {
|
||||
PTBlueService.listBatchNos(call.requireCurrentUser().id)
|
||||
PTBlueService.listBatchNos(call.requireCurrentUser())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +102,14 @@ fun Route.registerPTInvoiceRoutes() {
|
||||
val invoiceReqSerialNo = call.requiredQueryParameter("invoiceReqSerialNo", "请传入发票请求流水号")
|
||||
?: return@get
|
||||
call.respondPt("刷新发票状态失败") {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val account = com.bbit.ticket.service.piaotong.PTConfigService.requireDigitalAccountForAction(
|
||||
currentUser,
|
||||
call.request.queryParameters["digitalAccountId"],
|
||||
)
|
||||
PTBlueService.queryInvoiceAllInfo(
|
||||
QueryInvoiceRequest(
|
||||
taxpayerNum = call.requireCurrentUser().requirePtProfile().taxpayerNum,
|
||||
taxpayerNum = account.taxpayerNum,
|
||||
invoiceReqSerialNo = invoiceReqSerialNo,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.bbit.ticket.route.system
|
||||
import com.bbit.ticket.entity.common.ok
|
||||
import com.bbit.ticket.service.system.AuthService
|
||||
import com.bbit.ticket.entity.common.system.LoginRequest
|
||||
import com.bbit.ticket.entity.request.EnterpriseRegisterRequest
|
||||
import com.bbit.ticket.service.system.OperationLogService
|
||||
import com.bbit.ticket.utils.requireCurrentUser
|
||||
import io.ktor.server.auth.authenticate
|
||||
@@ -31,6 +32,19 @@ fun Route.registerAuthRoutes() {
|
||||
}
|
||||
}
|
||||
|
||||
post("/register-enterprise") {
|
||||
val start = TimeSource.Monotonic.markNow()
|
||||
val request = call.receive<EnterpriseRegisterRequest>()
|
||||
runCatching {
|
||||
val response = AuthService.registerEnterprise(request, call.request.local.remoteHost)
|
||||
call.respond(ok(response))
|
||||
OperationLogService.success(call, null, "REGISTER_ENTERPRISE", "企业注册成功", start.elapsedNow().inWholeMilliseconds)
|
||||
}.onFailure {
|
||||
OperationLogService.fail(call, null, "REGISTER_ENTERPRISE", "企业注册失败", it.message, start.elapsedNow().inWholeMilliseconds)
|
||||
throw it
|
||||
}
|
||||
}
|
||||
|
||||
authenticate("auth-jwt") {
|
||||
post("/logout") {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
|
||||
@@ -44,7 +44,12 @@ object OpenBlueInvoiceService {
|
||||
validateCreateRequest(request)
|
||||
val createRequest = request.withGeneratedInvoiceReqSerialNo(principal.userId)
|
||||
|
||||
PTBlueService.createBlueInvoice(createRequest.toAskInvoiceRequest(principal), principal.userId)
|
||||
PTBlueService.createBlueInvoice(
|
||||
createRequest.toAskInvoiceRequest(principal),
|
||||
principal.userId,
|
||||
principal.enterpriseId,
|
||||
principal.digitalAccountId,
|
||||
)
|
||||
val detail = PTBlueService.getInvoiceDetail(principal.userId, createRequest.requireInvoiceReqSerialNo())
|
||||
return OpenBlueInvoiceCreateResponse(
|
||||
requestNo = createRequest.requestNo,
|
||||
@@ -59,7 +64,13 @@ object OpenBlueInvoiceService {
|
||||
}
|
||||
|
||||
runCatching {
|
||||
PTBlueService.syncInvoiceFromPT(principal.userId, invoiceReqSerialNo, principal.taxPayerNum)
|
||||
PTBlueService.syncInvoiceFromPT(
|
||||
principal.userId,
|
||||
invoiceReqSerialNo,
|
||||
principal.taxPayerNum,
|
||||
principal.enterpriseId,
|
||||
principal.digitalAccountId,
|
||||
)
|
||||
}
|
||||
|
||||
val detail = PTBlueService.getInvoiceDetail(principal.userId, invoiceReqSerialNo)
|
||||
@@ -115,6 +126,8 @@ object OpenBlueInvoiceService {
|
||||
dbQuery {
|
||||
val batchId = OpenInvoiceBatchTable.insert {
|
||||
it[userId] = principal.userId
|
||||
it[enterpriseId] = principal.enterpriseId
|
||||
it[digitalAccountId] = principal.digitalAccountId
|
||||
it[batchNo] = request.batchNo
|
||||
it[totalCount] = request.items.size
|
||||
it[successCount] = 0
|
||||
@@ -241,6 +254,8 @@ object OpenBlueInvoiceService {
|
||||
principal.userId,
|
||||
item.invoiceReqSerialNo,
|
||||
principal.taxPayerNum,
|
||||
principal.enterpriseId,
|
||||
principal.digitalAccountId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
package com.bbit.ticket.service.piaotong
|
||||
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseTaxDao
|
||||
import com.bbit.ticket.database.system.SysUserTable.taxpayerNum
|
||||
import com.bbit.ticket.entity.request.AuthQrcodeRequest
|
||||
import com.bbit.ticket.entity.request.GetLoginSmsCodeRequest
|
||||
import com.bbit.ticket.entity.request.QueryDigitalAccountListRequest
|
||||
@@ -12,23 +10,16 @@ import com.bbit.ticket.entity.request.QueryEnterpriseInfoRequest
|
||||
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.TaxRegister
|
||||
import com.bbit.ticket.entity.request.TaxRegisterInfo
|
||||
import com.bbit.ticket.entity.request.TaxRegisterUserRequest
|
||||
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.LoginSmsCodeResponse
|
||||
import com.bbit.ticket.entity.response.QueryDigitalAccountListResponse
|
||||
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.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object PTAuthService {
|
||||
|
||||
@@ -41,34 +32,6 @@ object PTAuthService {
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记/删除 数电账号 2.3
|
||||
*/
|
||||
suspend fun registerUserFromPayload(req: TaxRegisterUserRequest, currentUser: CurrentUser): String {
|
||||
val res = PTClient.ptPost<TaxRegister, EtaxRegisterResponse>(
|
||||
"registerUser.pt", TaxRegister(
|
||||
taxpayerNum = req.taxpayerNum,
|
||||
account = req.taxAccount,
|
||||
password = currentUser.taxPassword ?: "",
|
||||
phoneNum = currentUser.phone ?: "",
|
||||
name = currentUser.realName ?: "",
|
||||
identityType = currentUser.taxIdentityType ?: "",
|
||||
)
|
||||
)
|
||||
dbQuery { EnterpriseTaxDao.updateUserAccount(currentUser.id, req.taxpayerNum, req.taxAccount) }
|
||||
return res.resultMsg
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册企业(纳税人) 2.2
|
||||
* 将企业信息注册到票通平台
|
||||
*/
|
||||
suspend fun registerEnterprise(req: TaxRegisterInfo, userId: Uuid): String {
|
||||
PTClient.ptPost<TaxRegisterInfo, EnterpriseTaxInfo>("register.pt", req)
|
||||
dbQuery { EnterpriseTaxDao.updateEnterpriseInfo(userId, req) }
|
||||
return "操作成功"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实名认证二维码 2.6
|
||||
*
|
||||
@@ -118,8 +81,8 @@ object PTAuthService {
|
||||
/**
|
||||
* 查询数电账号列表 2.45
|
||||
*/
|
||||
suspend fun getListTaxBureauAccount(req: QueryDigitalAccountListRequest): QueryDigitalAccountListResponse {
|
||||
return PTClient.ptPost<QueryDigitalAccountListRequest, QueryDigitalAccountListResponse>(
|
||||
suspend fun getListTaxBureauAccount(req: QueryDigitalAccountListRequest): List<DigitalAccountInfo> {
|
||||
return PTClient.ptPost<QueryDigitalAccountListRequest, List<DigitalAccountInfo>>(
|
||||
"listTaxBureauAccount.pt",
|
||||
req
|
||||
)
|
||||
@@ -132,4 +95,4 @@ object PTAuthService {
|
||||
return PTClient.ptPost<QueryEnterpriseBankAccountRequest, QueryEnterpriseBankAccountResponse>("queryEnterpriseBankInfo.pt", req)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,17 @@ 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.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.requirePtProfile
|
||||
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.PTClient
|
||||
import com.bbit.ticket.utils.parseUuid
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object PTBlueService {
|
||||
|
||||
suspend fun listBatchNos(userId: Uuid): List<String> =
|
||||
dbQuery { BlueInvoiceDao.listBatchNos(userId) }
|
||||
suspend fun listBatchNos(user: CurrentUser): List<String> =
|
||||
dbQuery { BlueInvoiceDao.listBatchNos(user.id) }
|
||||
|
||||
/**
|
||||
* 查询票通同步发票信息(支持插入和更新)
|
||||
@@ -31,12 +31,18 @@ object PTBlueService {
|
||||
* 既是蓝票/红票创建后的补充同步,
|
||||
* 也是手动刷新的核心方法。
|
||||
*/
|
||||
suspend fun syncInvoiceFromPT(userId: Uuid, invoiceReqSerialNo: String, taxpayerNum: String): QueryInvoiceResult {
|
||||
suspend fun syncInvoiceFromPT(
|
||||
userId: Uuid,
|
||||
invoiceReqSerialNo: String,
|
||||
taxpayerNum: String,
|
||||
enterpriseId: Uuid? = null,
|
||||
digitalAccountId: Uuid? = null,
|
||||
): QueryInvoiceResult {
|
||||
val res = PTClient.ptPost<QueryInvoiceRequest, GetInvoiceInfoResponse>(
|
||||
"queryInvoiceInfo.pt",
|
||||
QueryInvoiceRequest(taxpayerNum = taxpayerNum, invoiceReqSerialNo = invoiceReqSerialNo)
|
||||
)
|
||||
dbQuery { BlueInvoiceDao.upsertInvoiceInfo(userId, res) }
|
||||
dbQuery { BlueInvoiceDao.upsertInvoiceInfo(userId, res, enterpriseId, digitalAccountId) }
|
||||
val newStatus = when (res.code) {
|
||||
"0000" -> "SUCCESS"
|
||||
"7777" -> "PROCESSING"
|
||||
@@ -51,18 +57,25 @@ object PTBlueService {
|
||||
* 蓝票接口调用
|
||||
*/
|
||||
suspend fun invoiceBlue(req: AskInvoiceRequest, user: CurrentUser): String {
|
||||
val profile = user.requirePtProfile()
|
||||
val account = PTConfigService.requireDigitalAccountForAction(user, req.digitalAccountId)
|
||||
return createBlueInvoice(
|
||||
req.copy(
|
||||
taxpayerNum = profile.taxpayerNum,
|
||||
account = req.account ?: profile.taxAccount,
|
||||
taxpayerNum = account.taxpayerNum,
|
||||
account = account.account,
|
||||
),
|
||||
user.id,
|
||||
parseUuid(account.enterpriseId, "enterpriseId"),
|
||||
parseUuid(account.id, "digitalAccountId"),
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun createBlueInvoice(req: AskInvoiceRequest, userId: Uuid): String {
|
||||
dbQuery { BlueInvoiceDao.addInvoice(userId, req) }
|
||||
suspend fun createBlueInvoice(
|
||||
req: AskInvoiceRequest,
|
||||
userId: Uuid,
|
||||
enterpriseId: Uuid? = null,
|
||||
digitalAccountId: Uuid? = null,
|
||||
): String {
|
||||
dbQuery { BlueInvoiceDao.addInvoice(userId, req, enterpriseId, digitalAccountId) }
|
||||
try {
|
||||
PTClient.ptPost<AskInvoiceRequest, InvoiceCreateResponse>("invoiceBlue.pt", req)
|
||||
} catch (e: Exception) {
|
||||
@@ -71,7 +84,7 @@ object PTBlueService {
|
||||
}
|
||||
// 创建后立即同步一次(非关键,失败忽略)
|
||||
try {
|
||||
syncInvoiceFromPT(userId, req.invoiceReqSerialNo, req.taxpayerNum)
|
||||
syncInvoiceFromPT(userId, req.invoiceReqSerialNo, req.taxpayerNum, enterpriseId, digitalAccountId)
|
||||
} catch (_: Exception) { }
|
||||
return "操作成功"
|
||||
}
|
||||
@@ -80,14 +93,25 @@ object PTBlueService {
|
||||
* 分页查询开票历史(支持筛选)
|
||||
*/
|
||||
suspend fun getInvoiceBlueHistory(
|
||||
userId: Uuid,
|
||||
user: CurrentUser,
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
invoiceType: String? = null,
|
||||
isSuccess: Boolean? = null,
|
||||
batchNo: String? = null,
|
||||
): PageResult<InvoiceHistoryItem> =
|
||||
dbQuery { BlueInvoiceDao.invoiceHistory(userId, page, pageSize, invoiceType, isSuccess, batchNo) }
|
||||
dbQuery {
|
||||
BlueInvoiceDao.invoiceHistory(
|
||||
userId = user.id,
|
||||
enterpriseId = user.enterpriseId,
|
||||
digitalAccountId = if (user.isDigitalOperator) user.digitalAccountId else null,
|
||||
page = page,
|
||||
pageSize = pageSize,
|
||||
invoiceType = invoiceType,
|
||||
isSuccess = isSuccess,
|
||||
batchNo = batchNo,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询发票完整详情
|
||||
@@ -110,16 +134,16 @@ object PTBlueService {
|
||||
*/
|
||||
suspend fun queryInvoiceAllInfo(req: QueryInvoiceRequest): QueryInvoiceResult {
|
||||
val invoiceReqSerialNo = req.invoiceReqSerialNo
|
||||
val existing = dbQuery {
|
||||
BlueInvoiceDao.findUserIdBySerialNo(invoiceReqSerialNo)
|
||||
val scope = dbQuery {
|
||||
BlueInvoiceDao.findInvoiceScopeBySerialNo(invoiceReqSerialNo)
|
||||
}
|
||||
val result = syncInvoiceFromPT(existing, invoiceReqSerialNo, req.taxpayerNum)
|
||||
val result = syncInvoiceFromPT(scope.userId, invoiceReqSerialNo, req.taxpayerNum, scope.enterpriseId, scope.digitalAccountId)
|
||||
val relatedSerialNos = dbQuery {
|
||||
BlueInvoiceDao.findRelatedInvoiceReqSerialNos(existing, invoiceReqSerialNo)
|
||||
BlueInvoiceDao.findRelatedInvoiceReqSerialNos(scope.userId, invoiceReqSerialNo)
|
||||
}
|
||||
relatedSerialNos.forEach { relatedSerialNo ->
|
||||
runCatching {
|
||||
syncInvoiceFromPT(existing, relatedSerialNo, req.taxpayerNum)
|
||||
syncInvoiceFromPT(scope.userId, relatedSerialNo, req.taxpayerNum, scope.enterpriseId, scope.digitalAccountId)
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -2,54 +2,224 @@
|
||||
|
||||
package com.bbit.ticket.service.piaotong
|
||||
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseTaxDao
|
||||
import com.bbit.ticket.entity.request.UpdateDigitalAccountRequest
|
||||
import com.bbit.ticket.entity.request.UpdateEnterpriseInfoRequest
|
||||
import com.bbit.ticket.entity.request.UpdatePresetDataRequest
|
||||
import com.bbit.ticket.entity.response.DigitalAccountResponse
|
||||
import com.bbit.ticket.entity.response.EnterpriseInfoResponse
|
||||
import com.bbit.ticket.entity.response.PresetDataResponse
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseManageDao
|
||||
import com.bbit.ticket.dao.system.UserDao
|
||||
import com.bbit.ticket.database.piaotong.PtDigitalAccountTable
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.entity.request.CreateDigitalAccountRequest
|
||||
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.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
|
||||
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.parseUuid
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object PTConfigService {
|
||||
private const val DIGITAL_OPERATOR_ROLE_CODE = "DIGITAL_OPERATOR"
|
||||
private const val DEFAULT_OPERATOR_PASSWORD = "123456"
|
||||
|
||||
// =============================================
|
||||
// 企业信息
|
||||
// =============================================
|
||||
|
||||
suspend fun getEnterpriseInfo(userId: Uuid): EnterpriseInfoResponse? = dbQuery {
|
||||
EnterpriseTaxDao.getEnterpriseInfo(userId)
|
||||
suspend fun getEnterpriseInfo(user: CurrentUser): EnterpriseManageResponse? = dbQuery {
|
||||
val enterpriseId = requireEnterpriseId(user)
|
||||
EnterpriseManageDao.enterpriseDetail(enterpriseId)
|
||||
}
|
||||
|
||||
suspend fun updateEnterpriseInfo(userId: Uuid, req: UpdateEnterpriseInfoRequest): String {
|
||||
dbQuery { EnterpriseTaxDao.updateEnterpriseInfoLocal(userId, req) }
|
||||
return "企业信息保存成功"
|
||||
suspend fun refreshEnterpriseInfo(user: CurrentUser): EnterpriseManageResponse {
|
||||
val enterpriseId = dbQuery { requireEnterpriseId(user) }
|
||||
val taxpayerNum = dbQuery {
|
||||
EnterpriseManageDao.enterpriseDetail(enterpriseId)?.taxpayerNum
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "企业信息不存在", HttpStatusCode.BadRequest)
|
||||
}
|
||||
val res = PTAuthService.getEnterpriseInfo(QueryEnterpriseInfoRequest(taxpayerNum))
|
||||
dbQuery { EnterpriseManageDao.updateEnterpriseFromPt(enterpriseId, res) }
|
||||
return dbQuery { EnterpriseManageDao.enterpriseDetail(enterpriseId)!! }
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 登记数电账号
|
||||
// =============================================
|
||||
|
||||
suspend fun getDigitalAccount(userId: Uuid): DigitalAccountResponse? = dbQuery {
|
||||
EnterpriseTaxDao.getDigitalAccount(userId)
|
||||
suspend fun updateInvoiceSetting(user: CurrentUser, req: UpdateInvoiceSettingRequest): String = dbQuery {
|
||||
EnterpriseManageDao.updateInvoiceSetting(requireEnterpriseId(user), req)
|
||||
"开票设置保存成功"
|
||||
}
|
||||
|
||||
suspend fun updateDigitalAccount(userId: Uuid, req: UpdateDigitalAccountRequest): String {
|
||||
dbQuery { EnterpriseTaxDao.updateDigitalAccountLocal(userId, req) }
|
||||
return "账号信息保存成功"
|
||||
suspend fun queryEnterpriseBankAccounts(user: CurrentUser): List<BankInfo> {
|
||||
val taxpayerNum = dbQuery {
|
||||
EnterpriseManageDao.enterpriseDetail(requireEnterpriseId(user))?.taxpayerNum
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "企业信息不存在", HttpStatusCode.BadRequest)
|
||||
}
|
||||
return PTAuthService.queryEnterpriseBankInfo(QueryEnterpriseBankAccountRequest(taxpayerNum)).bankList
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 开票预设数据
|
||||
// =============================================
|
||||
|
||||
suspend fun getPresetData(userId: Uuid): PresetDataResponse? = dbQuery {
|
||||
EnterpriseTaxDao.getPresetData(userId)
|
||||
suspend fun listDigitalAccounts(user: CurrentUser): List<DigitalAccountManageItem> = dbQuery {
|
||||
when {
|
||||
user.isSuperAdmin || user.isEnterpriseAdmin -> {
|
||||
EnterpriseManageDao.digitalAccountsForEnterprise(requireEnterpriseId(user))
|
||||
}
|
||||
user.isDigitalOperator -> {
|
||||
val id = user.digitalAccountId
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "当前账号未绑定数电账号")
|
||||
EnterpriseManageDao.digitalAccount(id)?.let { listOf(EnterpriseManageDao.run { it.toDigitalAccountItem() }) }
|
||||
?: emptyList()
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updatePresetData(userId: Uuid, req: UpdatePresetDataRequest): String {
|
||||
dbQuery { EnterpriseTaxDao.updatePresetData(userId, req) }
|
||||
return "预设数据保存成功"
|
||||
suspend fun refreshDigitalAccounts(user: CurrentUser): List<DigitalAccountManageItem> {
|
||||
val enterprise = dbQuery {
|
||||
val enterpriseId = requireEnterpriseId(user)
|
||||
EnterpriseManageDao.enterpriseDetail(enterpriseId)
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "企业信息不存在", HttpStatusCode.BadRequest)
|
||||
}
|
||||
val res = PTAuthService.getListTaxBureauAccount(QueryDigitalAccountListRequest(enterprise.taxpayerNum))
|
||||
dbQuery {
|
||||
val enterpriseId = parseUuid(enterprise.id, "enterpriseId")
|
||||
res.forEach { item ->
|
||||
val digitalAccountId = EnterpriseManageDao.upsertDigitalAccount(enterpriseId, item)
|
||||
ensureDigitalOperatorAccount(
|
||||
enterpriseId = enterpriseId,
|
||||
taxpayerNum = enterprise.taxpayerNum,
|
||||
item = item,
|
||||
digitalAccountId = digitalAccountId,
|
||||
platformPassword = DEFAULT_OPERATOR_PASSWORD,
|
||||
)
|
||||
}
|
||||
}
|
||||
return listDigitalAccounts(user)
|
||||
}
|
||||
|
||||
suspend fun createDigitalAccount(user: CurrentUser, req: CreateDigitalAccountRequest): DigitalAccountManageItem {
|
||||
if (!user.isSuperAdmin && !user.isEnterpriseAdmin) {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "无权新增数电账号", HttpStatusCode.Forbidden)
|
||||
}
|
||||
val enterprise = dbQuery {
|
||||
EnterpriseManageDao.enterpriseDetail(requireEnterpriseId(user))
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "企业信息不存在", HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
PTClient.ptPost<TaxRegister, EtaxRegisterResponse>(
|
||||
"registerUser.pt",
|
||||
TaxRegister(
|
||||
taxpayerNum = enterprise.taxpayerNum,
|
||||
account = req.account,
|
||||
password = req.taxPassword,
|
||||
phoneNum = req.phoneNum,
|
||||
name = req.name,
|
||||
identityType = req.identityType,
|
||||
)
|
||||
)
|
||||
|
||||
return dbQuery {
|
||||
val enterpriseId = parseUuid(enterprise.id, "enterpriseId")
|
||||
val userId = ensureDigitalOperatorAccount(
|
||||
enterpriseId = enterpriseId,
|
||||
taxpayerNum = enterprise.taxpayerNum,
|
||||
account = req.account.trim(),
|
||||
name = req.name.trim(),
|
||||
phone = req.phoneNum.trim().ifBlank { null },
|
||||
digitalAccountId = null,
|
||||
platformPassword = req.platformPassword,
|
||||
)
|
||||
|
||||
val digitalAccountId = EnterpriseManageDao.createDigitalAccount(
|
||||
enterpriseId = enterpriseId,
|
||||
taxpayerNum = enterprise.taxpayerNum,
|
||||
account = req.account.trim(),
|
||||
name = req.name.trim(),
|
||||
identityType = req.identityType.trim(),
|
||||
platformUserId = userId,
|
||||
apiKey = ApiKeyUtil.generate(),
|
||||
)
|
||||
EnterpriseManageDao.bindDigitalAccountUser(digitalAccountId, userId)
|
||||
EnterpriseManageDao.digitalAccount(digitalAccountId)!!.let { EnterpriseManageDao.run { it.toDigitalAccountItem() } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureDigitalOperatorAccount(
|
||||
enterpriseId: Uuid,
|
||||
taxpayerNum: String,
|
||||
item: DigitalAccountInfo,
|
||||
digitalAccountId: Uuid,
|
||||
platformPassword: String,
|
||||
): Uuid =
|
||||
ensureDigitalOperatorAccount(
|
||||
enterpriseId = enterpriseId,
|
||||
taxpayerNum = taxpayerNum,
|
||||
account = item.account,
|
||||
name = item.name,
|
||||
phone = null,
|
||||
digitalAccountId = digitalAccountId,
|
||||
platformPassword = platformPassword,
|
||||
)
|
||||
|
||||
private fun ensureDigitalOperatorAccount(
|
||||
enterpriseId: Uuid,
|
||||
taxpayerNum: String,
|
||||
account: String,
|
||||
name: String?,
|
||||
phone: String?,
|
||||
digitalAccountId: Uuid?,
|
||||
platformPassword: String,
|
||||
): Uuid {
|
||||
val username = "${taxpayerNum}_${account.trim()}"
|
||||
val existing = UserDao.findByUsername(username)
|
||||
val userId = existing?.get(SysUserTable.id)
|
||||
?: UserDao.createPlatformUser(
|
||||
username = username,
|
||||
passwordHash = PasswordService.hash(platformPassword),
|
||||
nickname = name,
|
||||
realName = name,
|
||||
phone = phone,
|
||||
enterpriseId = enterpriseId,
|
||||
digitalAccountId = null,
|
||||
userType = "DIGITAL_OPERATOR",
|
||||
).also { createdUserId ->
|
||||
UserDao.findEnabledRoleIdByCode(DIGITAL_OPERATOR_ROLE_CODE)?.let { roleId ->
|
||||
UserDao.replaceRoles(createdUserId, listOf(roleId))
|
||||
}
|
||||
}
|
||||
|
||||
if (digitalAccountId != null) {
|
||||
val row = EnterpriseManageDao.digitalAccount(digitalAccountId)
|
||||
val apiKey = row?.get(PtDigitalAccountTable.apiKey) ?: ApiKeyUtil.generate()
|
||||
if (row?.get(PtDigitalAccountTable.platformUserId) != userId || row[PtDigitalAccountTable.apiKey].isNullOrBlank()) {
|
||||
EnterpriseManageDao.bindDigitalAccountUserAndApiKey(digitalAccountId, userId, apiKey)
|
||||
}
|
||||
}
|
||||
return userId
|
||||
}
|
||||
|
||||
suspend fun openApiStatistics(user: CurrentUser): List<OpenApiStatisticsItem> = dbQuery {
|
||||
EnterpriseManageDao.openApiStatistics(requireEnterpriseId(user), if (user.isDigitalOperator) user.digitalAccountId else null)
|
||||
}
|
||||
|
||||
fun requireEnterpriseId(user: CurrentUser): Uuid =
|
||||
user.enterpriseId ?: throw BizException(ErrorCode.BAD_REQUEST.code, "当前账号未绑定企业")
|
||||
|
||||
suspend fun requireDigitalAccountForAction(user: CurrentUser, digitalAccountId: String?): DigitalAccountManageItem = dbQuery {
|
||||
val targetId = when {
|
||||
user.isDigitalOperator -> user.digitalAccountId
|
||||
!digitalAccountId.isNullOrBlank() -> parseUuid(digitalAccountId, "digitalAccountId")
|
||||
else -> user.digitalAccountId
|
||||
} ?: throw BizException(ErrorCode.BAD_REQUEST.code, "请选择数电账号")
|
||||
|
||||
val row = EnterpriseManageDao.digitalAccount(targetId)
|
||||
?: throw BizException(ErrorCode.BAD_REQUEST.code, "数电账号不存在", HttpStatusCode.NotFound)
|
||||
if (!user.isSuperAdmin && row[PtDigitalAccountTable.enterpriseId] != requireEnterpriseId(user)) {
|
||||
throw BizException(ErrorCode.FORBIDDEN.code, "无权操作该数电账号", HttpStatusCode.Forbidden)
|
||||
}
|
||||
EnterpriseManageDao.run { row.toDigitalAccountItem() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ 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.requirePtProfile
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import com.bbit.ticket.utils.parseUuid
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsBytes
|
||||
import kotlin.uuid.Uuid
|
||||
@@ -28,14 +28,14 @@ object PTRedService {
|
||||
* 红票接口调用
|
||||
*/
|
||||
suspend fun invoiceRed(user: CurrentUser,req:RedCreateRequest): String {
|
||||
val profile = user.requirePtProfile()
|
||||
val account = PTConfigService.requireDigitalAccountForAction(user, null)
|
||||
val invoiceReqSerialNo = PTClient.ptDate()
|
||||
val historyId = Uuid.parse(req.historyId)
|
||||
val his = dbQuery {
|
||||
HistoryDao.findByHistory(historyId, user.id)
|
||||
}
|
||||
val req = QuickRedInvoiceRequest(
|
||||
taxpayerNum = profile.taxpayerNum,
|
||||
taxpayerNum = account.taxpayerNum,
|
||||
invoiceReqSerialNo = invoiceReqSerialNo,
|
||||
invoiceCode = his.invoiceCode,
|
||||
invoiceNo = his.invoiceNo,
|
||||
@@ -43,7 +43,7 @@ object PTRedService {
|
||||
blueInvoiceDate = his.blueInvoiceDate,
|
||||
redReason = req.redReason,
|
||||
amount = his.totalAmount?.negate()?.toPlainString()?:"0.0",
|
||||
account = profile.taxAccount,
|
||||
account = account.account,
|
||||
invoiceKind = his.invoiceKind,
|
||||
takerName = req.takerName,
|
||||
takerTel = req.takerTel,
|
||||
@@ -51,8 +51,10 @@ object PTRedService {
|
||||
)
|
||||
PTClient.ptPost<QuickRedInvoiceRequest, QuickRedInvoiceResponse>("invoiceRed.pt", req)
|
||||
dbQuery { RedInvoiceDao.addRedInvoice(user.id, historyId, req) }
|
||||
PTBlueService.syncInvoiceFromPT(user.id, his.invoiceReqSerialNo, profile.taxpayerNum)
|
||||
PTBlueService.syncInvoiceFromPT(user.id, invoiceReqSerialNo, profile.taxpayerNum)
|
||||
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)
|
||||
return "操作成功"
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.bbit.ticket.dao.system.LogDao
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object ApiAccessLogService {
|
||||
/**
|
||||
@@ -25,6 +26,9 @@ object ApiAccessLogService {
|
||||
call: ApplicationCall,
|
||||
appKey: String?,
|
||||
appName: String?,
|
||||
enterpriseId: Uuid? = null,
|
||||
digitalAccountId: Uuid? = null,
|
||||
interfaceCode: String? = null,
|
||||
requestBody: String?,
|
||||
responseCode: String?,
|
||||
responseBody: String?,
|
||||
@@ -36,6 +40,9 @@ object ApiAccessLogService {
|
||||
call = call,
|
||||
appKey = appKey,
|
||||
appName = appName,
|
||||
enterpriseId = enterpriseId,
|
||||
digitalAccountId = digitalAccountId,
|
||||
interfaceCode = interfaceCode,
|
||||
requestBody = requestBody,
|
||||
responseCode = responseCode,
|
||||
responseBody = responseBody,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package com.bbit.ticket.service.system
|
||||
|
||||
import com.bbit.ticket.dao.system.MenuDao
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseManageDao
|
||||
import com.bbit.ticket.dao.system.UserDao
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.entity.common.BizException
|
||||
@@ -11,12 +12,19 @@ import com.bbit.ticket.entity.common.system.CurrentUserProfile
|
||||
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 io.ktor.http.HttpStatusCode
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
|
||||
object AuthService {
|
||||
private const val ENTERPRISE_ADMIN_ROLE_CODE = "ENTERPRISE_ADMIN"
|
||||
|
||||
suspend fun login(request: LoginRequest, loginIp: String?): LoginResponse {
|
||||
val username = request.username.trim()
|
||||
if (username.isBlank() || request.password.isBlank()) {
|
||||
@@ -57,6 +65,55 @@ object AuthService {
|
||||
return LoginResponse(accessToken = accessToken, expiresIn = expiresIn)
|
||||
}
|
||||
|
||||
suspend fun registerEnterprise(request: EnterpriseRegisterRequest, loginIp: String?): LoginResponse {
|
||||
val taxpayerNum = request.taxpayerNum.trim()
|
||||
if (taxpayerNum.isBlank() || request.enterpriseName.isBlank()) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "纳税人识别号和企业名称不能为空", HttpStatusCode.BadRequest)
|
||||
}
|
||||
if (request.regionCode.isBlank()) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "地区编码不能为空", HttpStatusCode.BadRequest)
|
||||
}
|
||||
if (request.contactsPhone.isBlank()) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "联系人手机不能为空", HttpStatusCode.BadRequest)
|
||||
}
|
||||
if (request.password.isBlank()) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "密码不能为空", HttpStatusCode.BadRequest)
|
||||
}
|
||||
if (request.password != request.confirmPassword) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "两次输入的密码不一致", HttpStatusCode.BadRequest)
|
||||
}
|
||||
|
||||
val registerInfo = request.toTaxRegisterInfo()
|
||||
dbQuery {
|
||||
if (UserDao.findByUsername(taxpayerNum) != null) {
|
||||
throw BizException(ErrorCode.DATA_CONFLICT.code, "该纳税人识别号已注册", HttpStatusCode.Conflict)
|
||||
}
|
||||
if (EnterpriseManageDao.findEnterpriseByTaxpayerNum(taxpayerNum) != null) {
|
||||
throw BizException(ErrorCode.DATA_CONFLICT.code, "企业已存在", HttpStatusCode.Conflict)
|
||||
}
|
||||
}
|
||||
PTClient.ptPost<TaxRegisterInfo, EnterpriseTaxInfo>("register.pt", registerInfo)
|
||||
|
||||
dbQuery {
|
||||
val enterpriseId = EnterpriseManageDao.createEnterprise(registerInfo)
|
||||
val userId = UserDao.createPlatformUser(
|
||||
username = taxpayerNum,
|
||||
passwordHash = PasswordService.hash(request.password),
|
||||
nickname = request.enterpriseName.trim(),
|
||||
realName = request.legalPersonName?.trim()?.ifBlank { null },
|
||||
phone = request.contactsPhone.trim(),
|
||||
enterpriseId = enterpriseId,
|
||||
digitalAccountId = null,
|
||||
userType = "ENTERPRISE_ADMIN",
|
||||
)
|
||||
UserDao.findEnabledRoleIdByCode(ENTERPRISE_ADMIN_ROLE_CODE)?.let { roleId ->
|
||||
UserDao.replaceRoles(userId, listOf(roleId))
|
||||
}
|
||||
}
|
||||
|
||||
return login(LoginRequest(username = taxpayerNum, password = request.password), loginIp)
|
||||
}
|
||||
|
||||
suspend fun me(currentUser: CurrentUser): MeResponse {
|
||||
val userRow = dbQuery { UserDao.requireActive(currentUser.id) }
|
||||
|
||||
@@ -73,12 +130,11 @@ object AuthService {
|
||||
phone = userRow[SysUserTable.phone],
|
||||
email = userRow[SysUserTable.email],
|
||||
orgId = userRow[SysUserTable.orgId]?.toString(),
|
||||
enterpriseId = userRow[SysUserTable.enterpriseId]?.toString(),
|
||||
digitalAccountId = userRow[SysUserTable.digitalAccountId]?.toString(),
|
||||
userType = userRow[SysUserTable.userType],
|
||||
status = userRow[SysUserTable.status],
|
||||
createdAt = userRow[SysUserTable.createdAt]?.toString(),
|
||||
taxpayerNum = userRow[SysUserTable.taxpayerNum],
|
||||
account = userRow[SysUserTable.taxAccount],
|
||||
taxPassword = userRow[SysUserTable.taxPassword],
|
||||
taxIdentityType = userRow[SysUserTable.taxIdentityType],
|
||||
createdAt = userRow[SysUserTable.createdAt].toString(),
|
||||
),
|
||||
menus = menuTree,
|
||||
permissions = permissions,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
package com.bbit.ticket.utils
|
||||
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseManageDao
|
||||
import com.bbit.ticket.database.piaotong.PtDigitalAccountTable
|
||||
import com.bbit.ticket.entity.common.BizException
|
||||
import com.bbit.ticket.entity.common.ErrorCode
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
@@ -21,6 +23,8 @@ data class OpenApiPrincipal(
|
||||
val userId: Uuid,
|
||||
val username: String,
|
||||
val apiKey: String,
|
||||
val enterpriseId: Uuid,
|
||||
val digitalAccountId: Uuid,
|
||||
val taxPayerNum: String,
|
||||
val taxAccount: String,
|
||||
)
|
||||
@@ -32,47 +36,32 @@ suspend fun ApplicationCall.requireOpenApiPrincipal(): OpenApiPrincipal {
|
||||
throw BizException(ErrorCode.UNAUTHORIZED.code, "缺少 X-Api-Key", HttpStatusCode.Unauthorized)
|
||||
}
|
||||
|
||||
val row = dbQuery {
|
||||
SysUserTable.selectAll()
|
||||
.where {
|
||||
(SysUserTable.apiKey eq apiKey) and
|
||||
(SysUserTable.status eq "ENABLED") and
|
||||
SysUserTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull()
|
||||
} ?: throw BizException(ErrorCode.UNAUTHORIZED.code, "API Key 无效或已停用", HttpStatusCode.Unauthorized)
|
||||
val accountRow = dbQuery { EnterpriseManageDao.digitalAccountByApiKey(apiKey) }
|
||||
?: throw BizException(ErrorCode.UNAUTHORIZED.code, "API Key 无效或已停用", HttpStatusCode.Unauthorized)
|
||||
val userId = accountRow[PtDigitalAccountTable.platformUserId]
|
||||
?: throw BizException(ErrorCode.UNAUTHORIZED.code, "API Key 未绑定平台账号", HttpStatusCode.Unauthorized)
|
||||
|
||||
val taxpayerNum = row[SysUserTable.taxpayerNum]?.takeIf { it.isNotBlank() }
|
||||
val taxAccount = row[SysUserTable.taxAccount]?.takeIf { it.isNotBlank() }
|
||||
if (taxpayerNum == null || taxAccount == null) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "请先完善用户信息")
|
||||
}
|
||||
val row = dbQuery {
|
||||
SysUserTable.selectAll().where {
|
||||
(SysUserTable.id eq userId) and
|
||||
(SysUserTable.status eq "ENABLED") and
|
||||
SysUserTable.deletedAt.isNull()
|
||||
}.singleOrNull()
|
||||
} ?: throw BizException(ErrorCode.UNAUTHORIZED.code, "API Key 绑定账号无效或已停用", HttpStatusCode.Unauthorized)
|
||||
|
||||
dbQuery {
|
||||
SysUserTable.update({ SysUserTable.id eq row[SysUserTable.id] }) {
|
||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||
it[updatedAt] = OffsetDateTime.now()
|
||||
}
|
||||
}
|
||||
|
||||
return OpenApiPrincipal(
|
||||
userId = row[SysUserTable.id],
|
||||
userId = userId,
|
||||
username = row[SysUserTable.username],
|
||||
apiKey = apiKey,
|
||||
taxPayerNum = taxpayerNum,
|
||||
taxAccount = taxAccount,
|
||||
enterpriseId = accountRow[PtDigitalAccountTable.enterpriseId],
|
||||
digitalAccountId = accountRow[PtDigitalAccountTable.id],
|
||||
taxPayerNum = accountRow[PtDigitalAccountTable.taxpayerNum],
|
||||
taxAccount = accountRow[PtDigitalAccountTable.account],
|
||||
)
|
||||
}
|
||||
|
||||
data class PtProfile(
|
||||
val taxpayerNum: String,
|
||||
val taxAccount: String,
|
||||
)
|
||||
|
||||
fun CurrentUser.requirePtProfile(): PtProfile {
|
||||
val taxpayerNum = taxPayerNum?.takeIf { it.isNotBlank() }
|
||||
val account = taxAccount?.takeIf { it.isNotBlank() }
|
||||
if (taxpayerNum == null || account == null) {
|
||||
throw BizException(ErrorCode.BAD_REQUEST.code, "请先完善用户信息")
|
||||
}
|
||||
return PtProfile(taxpayerNum, account)
|
||||
}
|
||||
|
||||
@@ -28,18 +28,23 @@ data class CurrentUser(
|
||||
val id: Uuid,
|
||||
val username: String,
|
||||
val orgId: Uuid?,
|
||||
val enterpriseId: Uuid?,
|
||||
val digitalAccountId: Uuid?,
|
||||
val userType: String,
|
||||
val tokenVersion: Int,
|
||||
val roleCodes: Set<String>,
|
||||
val permissions: Set<String>,
|
||||
val taxPayerNum: String?,
|
||||
val taxAccount: String?,
|
||||
val phone: String?,
|
||||
val realName: String?,
|
||||
val taxPassword: String?,
|
||||
val taxIdentityType: String?,
|
||||
) {
|
||||
val isSuperAdmin: Boolean
|
||||
get() = roleCodes.contains("SUPER_ADMIN")
|
||||
|
||||
val isEnterpriseAdmin: Boolean
|
||||
get() = userType == "ENTERPRISE_ADMIN"
|
||||
|
||||
val isDigitalOperator: Boolean
|
||||
get() = userType == "DIGITAL_OPERATOR"
|
||||
}
|
||||
|
||||
private val CurrentUserKey = AttributeKey<CurrentUser>("currentUser")
|
||||
@@ -138,13 +143,12 @@ suspend fun ApplicationCall.requireCurrentUser(): CurrentUser {
|
||||
id = userRow[SysUserTable.id],
|
||||
username = userRow[SysUserTable.username],
|
||||
orgId = userRow[SysUserTable.orgId],
|
||||
enterpriseId = userRow[SysUserTable.enterpriseId],
|
||||
digitalAccountId = userRow[SysUserTable.digitalAccountId],
|
||||
userType = userRow[SysUserTable.userType],
|
||||
tokenVersion = userRow[SysUserTable.tokenVersion],
|
||||
roleCodes = roleCodes,
|
||||
permissions = permissions,
|
||||
taxPayerNum = userRow[SysUserTable.taxpayerNum],
|
||||
taxAccount = userRow[SysUserTable.taxAccount],
|
||||
taxPassword = userRow[SysUserTable.taxPassword],
|
||||
taxIdentityType = userRow[SysUserTable.taxIdentityType],
|
||||
phone = userRow[SysUserTable.phone],
|
||||
realName = userRow[SysUserTable.realName],
|
||||
)
|
||||
|
||||
@@ -7,6 +7,8 @@ import com.bbit.ticket.database.piaotong.HistoryInvoiceVoucherTable
|
||||
import com.bbit.ticket.database.piaotong.HistoryInvoiceRedTable
|
||||
import com.bbit.ticket.database.piaotong.OpenInvoiceBatchItemTable
|
||||
import com.bbit.ticket.database.piaotong.OpenInvoiceBatchTable
|
||||
import com.bbit.ticket.database.piaotong.PtDigitalAccountTable
|
||||
import com.bbit.ticket.database.piaotong.PtEnterpriseTable
|
||||
import com.bbit.ticket.database.system.SysApiAccessLogTable
|
||||
import com.bbit.ticket.database.system.SysDictItemTable
|
||||
import com.bbit.ticket.database.system.SysDictTypeTable
|
||||
@@ -36,6 +38,8 @@ object DatabaseInitializer {
|
||||
SysDictItemTable,
|
||||
SysOperationLogTable,
|
||||
SysApiAccessLogTable,
|
||||
PtEnterpriseTable,
|
||||
PtDigitalAccountTable,
|
||||
HistoryInvoiceRedTable,
|
||||
HistoryInvoiceBasicTable,
|
||||
HistoryInvoiceGoodsTable,
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.bbit.ticket.utils.bootstrap
|
||||
|
||||
object Global {
|
||||
|
||||
val isDev = true
|
||||
val isDev = false
|
||||
|
||||
// 请求基础地址
|
||||
var baseUrl: String
|
||||
|
||||
@@ -5,7 +5,6 @@ package com.bbit.ticket.utils.bootstrap
|
||||
import com.bbit.ticket.database.system.*
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.service.system.PasswordService
|
||||
import com.bbit.ticket.utils.net.SecurityUtil
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.inList
|
||||
@@ -25,10 +24,11 @@ object SeedData {
|
||||
|
||||
const val ADMIN_USERNAME = "admin"
|
||||
const val ADMIN_INIT_PASSWORD = "Admin@123456"
|
||||
const val ADMIN_INIT_API_KEY = "tk_admin_test_key_please_change"
|
||||
|
||||
private const val DEFAULT_ORG_CODE = "DEFAULT_ORG"
|
||||
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"
|
||||
|
||||
// =========================================================
|
||||
// Main entry
|
||||
@@ -38,10 +38,24 @@ object SeedData {
|
||||
val now = OffsetDateTime.now()
|
||||
val orgId = upsertDefaultOrg(now)
|
||||
val roleId = upsertSuperAdminRole(now)
|
||||
val enterpriseAdminRoleId = upsertBusinessRole(
|
||||
code = ENTERPRISE_ADMIN_ROLE_CODE,
|
||||
name = "企业管理员",
|
||||
description = "外部注册企业默认管理员角色",
|
||||
now = now,
|
||||
)
|
||||
val digitalOperatorRoleId = upsertBusinessRole(
|
||||
code = DIGITAL_OPERATOR_ROLE_CODE,
|
||||
name = "开票员",
|
||||
description = "数电账号对应的平台开票员角色",
|
||||
now = now,
|
||||
)
|
||||
val adminId = upsertAdminUser(orgId, now)
|
||||
upsertUserRole(adminId, roleId)
|
||||
val menuIds = upsertMenus(now)
|
||||
bindRoleMenus(roleId, menuIds)
|
||||
bindRoleMenus(roleId, menuIds.values.toList())
|
||||
bindRoleMenus(enterpriseAdminRoleId, enterpriseAdminMenuIds(menuIds))
|
||||
bindRoleMenus(digitalOperatorRoleId, digitalOperatorMenuIds(menuIds))
|
||||
seedDicts(now)
|
||||
logger.info("Seed data initialized, default admin username: {}", ADMIN_USERNAME)
|
||||
}
|
||||
@@ -105,6 +119,39 @@ object SeedData {
|
||||
inserted[SysRoleTable.id]
|
||||
}
|
||||
|
||||
private suspend fun upsertBusinessRole(
|
||||
code: String,
|
||||
name: String,
|
||||
description: String,
|
||||
now: OffsetDateTime,
|
||||
): Uuid = dbQuery {
|
||||
val existing = SysRoleTable.selectAll()
|
||||
.where { (SysRoleTable.code eq code) and SysRoleTable.deletedAt.isNull() }
|
||||
.singleOrNull()
|
||||
|
||||
if (existing != null) {
|
||||
val id = existing[SysRoleTable.id]
|
||||
SysRoleTable.update({ SysRoleTable.id eq id }) {
|
||||
it[SysRoleTable.name] = name
|
||||
it[SysRoleTable.description] = description
|
||||
it[SysRoleTable.status] = "ENABLED"
|
||||
it[SysRoleTable.dataScope] = "SELF"
|
||||
it[SysRoleTable.updatedAt] = now
|
||||
}
|
||||
return@dbQuery id
|
||||
}
|
||||
|
||||
val inserted = SysRoleTable.insert {
|
||||
it[SysRoleTable.name] = name
|
||||
it[SysRoleTable.code] = code
|
||||
it[SysRoleTable.description] = description
|
||||
it[SysRoleTable.status] = "ENABLED"
|
||||
it[SysRoleTable.dataScope] = "SELF"
|
||||
it[SysRoleTable.createdAt] = now
|
||||
}
|
||||
inserted[SysRoleTable.id]
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Admin user
|
||||
// =========================================================
|
||||
@@ -120,14 +167,9 @@ object SeedData {
|
||||
it[SysUserTable.nickname] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
if (existing[SysUserTable.apiKey].isNullOrBlank()) {
|
||||
it[SysUserTable.apiKey] = ADMIN_INIT_API_KEY
|
||||
}
|
||||
it[SysUserTable.userType] = "SYSTEM"
|
||||
it[SysUserTable.updatedAt] = now
|
||||
it[SysUserTable.taxpayerNum] = "500102201007206608"
|
||||
it[SysUserTable.phone] = "13000000000"
|
||||
it[SysUserTable.taxIdentityType] = "01"
|
||||
it[SysUserTable.taxPassword] = SecurityUtil.encrypt3DES(Global.ptPassword, "ispassword")
|
||||
it[SysUserTable.realName] = "测试"
|
||||
}
|
||||
return@dbQuery id
|
||||
@@ -139,13 +181,9 @@ object SeedData {
|
||||
it[SysUserTable.nickname] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
it[SysUserTable.apiKey] = ADMIN_INIT_API_KEY
|
||||
it[SysUserTable.userType] = "SYSTEM"
|
||||
it[SysUserTable.tokenVersion] = 1
|
||||
it[SysUserTable.taxpayerNum] = "500102201007206608"
|
||||
it[SysUserTable.phone] = "13000000000"
|
||||
it[SysUserTable.taxAccount] = "DEMOadmin"
|
||||
it[SysUserTable.taxIdentityType] = "01"
|
||||
it[SysUserTable.taxPassword] = SecurityUtil.encrypt3DES(Global.ptPassword, "ispassword")
|
||||
it[SysUserTable.realName] = "测试"
|
||||
it[SysUserTable.createdAt] = now
|
||||
}
|
||||
@@ -168,7 +206,7 @@ object SeedData {
|
||||
// Menus & permissions
|
||||
// =========================================================
|
||||
|
||||
private suspend fun upsertMenus(now: OffsetDateTime): List<Uuid> {
|
||||
private suspend fun upsertMenus(now: OffsetDateTime): Map<String, Uuid> {
|
||||
val seedMenus = listOf(
|
||||
rootMenu("dashboard", "工作台", "Dashboard", "/dashboard", "dashboard/index", "LayoutDashboard", 10),
|
||||
catalog("system", "系统管理", "SystemRoot", "Settings", 20),
|
||||
@@ -196,10 +234,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),
|
||||
catalog("piaotong", "票通服务", "PiaoTongRoot", "Receipt", 40),
|
||||
subMenu("piaotong_info", "piaotong", "基础信息", "PiaoTongInfo", "/piaotong/info", "piaotong/index", "User", "piaotong:info:view", 10),
|
||||
subMenu("piaotong_invoice_issue", "piaotong", "开具蓝票", "PiaoTongInvoiceIssue", "/piaotong/invoice-issue", "piaotong/invoice-issue/index", "FilePlus", "piaotong:invoice-issue:view", 20),
|
||||
subMenu("piaotong_invoice_history", "piaotong", "开票历史", "PiaoTongInvoiceHistory", "/piaotong/invoice-history", "piaotong/invoice-history/index", "History", "piaotong:invoice-history: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),
|
||||
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>()
|
||||
@@ -208,9 +251,35 @@ object SeedData {
|
||||
val menuId = upsertMenu(menu, parentId, now)
|
||||
idMap[menu.key] = menuId
|
||||
}
|
||||
return idMap.values.toList()
|
||||
return idMap
|
||||
}
|
||||
|
||||
private fun enterpriseAdminMenuIds(menuIds: Map<String, Uuid>): List<Uuid> =
|
||||
listOf(
|
||||
"dashboard",
|
||||
"basic_info",
|
||||
"enterprise_info",
|
||||
"digital_account",
|
||||
"invoice_setting",
|
||||
"invoice_service",
|
||||
"piaotong_invoice_issue",
|
||||
"piaotong_invoice_history",
|
||||
"statistics_info",
|
||||
"openapi_statistics",
|
||||
).mapNotNull { menuIds[it] }
|
||||
|
||||
private fun digitalOperatorMenuIds(menuIds: Map<String, Uuid>): List<Uuid> =
|
||||
listOf(
|
||||
"dashboard",
|
||||
"basic_info",
|
||||
"digital_account",
|
||||
"invoice_service",
|
||||
"piaotong_invoice_issue",
|
||||
"piaotong_invoice_history",
|
||||
"statistics_info",
|
||||
"openapi_statistics",
|
||||
).mapNotNull { menuIds[it] }
|
||||
|
||||
private suspend fun upsertMenu(seedMenu: SeedMenu, parentId: Uuid?, now: OffsetDateTime): Uuid = dbQuery {
|
||||
val existing = SysMenuTable.selectAll()
|
||||
.where { (SysMenuTable.name eq seedMenu.name) and SysMenuTable.deletedAt.isNull() }
|
||||
|
||||
@@ -12,19 +12,34 @@ import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URI
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
object PTClient {
|
||||
|
||||
val logger = LoggerFactory.getLogger(PTClient::class.java)
|
||||
private const val CONNECT_TIMEOUT_MS = 10_000
|
||||
private const val READ_TIMEOUT_MS = 60_000
|
||||
val wireJson = Json {
|
||||
explicitNulls = false
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
val client = HttpClient(CIO) {
|
||||
|
||||
@@ -123,14 +138,15 @@ object PTClient {
|
||||
headers: Map<String, String> = emptyMap()
|
||||
): Resp {
|
||||
// req json
|
||||
val reqJson = myJson.encodeToString(body)
|
||||
val reqJson = wireJson.encodeToString(body)
|
||||
|
||||
val targetUrl = Global.baseUrl + url
|
||||
val requestBody = buildRequestData(reqJson)
|
||||
val startedAt = System.currentTimeMillis()
|
||||
logger.info("POST url = $targetUrl")
|
||||
logger.info("req = $reqJson")
|
||||
val response = client.post(Global.baseUrl + url) {
|
||||
contentType(ContentType.Application.Json)
|
||||
headers.forEach { (k, v) -> header(k, v) }
|
||||
setBody(buildRequestData(reqJson))
|
||||
}.bodyAsText()
|
||||
val response = postJsonRaw(targetUrl, requestBody, headers)
|
||||
logger.info("raw response costMs=${System.currentTimeMillis() - startedAt} body=$response")
|
||||
|
||||
val decrypted = disposeResponse(response)
|
||||
val result = myJson.decodeFromString<PTResponse<JsonElement>>(decrypted)
|
||||
@@ -145,6 +161,54 @@ object PTClient {
|
||||
return myJson.decodeFromJsonElement<Resp>(result.content!!)
|
||||
}
|
||||
|
||||
suspend fun postJsonRaw(
|
||||
url: String,
|
||||
jsonBody: String,
|
||||
headers: Map<String, String> = emptyMap(),
|
||||
): String = withContext(Dispatchers.IO) {
|
||||
var connection: HttpURLConnection? = null
|
||||
try {
|
||||
val bytes = jsonBody.toByteArray(Charsets.UTF_8)
|
||||
connection = (URI.create(url).toURL().openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "POST"
|
||||
connectTimeout = CONNECT_TIMEOUT_MS
|
||||
readTimeout = READ_TIMEOUT_MS
|
||||
doOutput = true
|
||||
useCaches = false
|
||||
setRequestProperty("Content-Type", "application/json; charset=UTF-8")
|
||||
setRequestProperty("Accept", "application/json")
|
||||
setRequestProperty("Connection", "close")
|
||||
headers.forEach { (k, v) -> setRequestProperty(k, v) }
|
||||
setFixedLengthStreamingMode(bytes.size)
|
||||
}
|
||||
|
||||
connection.outputStream.use { output ->
|
||||
output.write(bytes)
|
||||
output.flush()
|
||||
}
|
||||
|
||||
val responseCode = connection.responseCode
|
||||
val stream = if (responseCode in 200..299) connection.inputStream else connection.errorStream
|
||||
val response = stream?.use { input ->
|
||||
BufferedReader(InputStreamReader(input, Charsets.UTF_8)).use { reader ->
|
||||
buildString {
|
||||
while (true) {
|
||||
val line = reader.readLine() ?: break
|
||||
append(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.orEmpty()
|
||||
|
||||
if (responseCode !in 200..299) {
|
||||
throw IllegalStateException("票通接口 HTTP $responseCode: $response")
|
||||
}
|
||||
response
|
||||
} finally {
|
||||
connection?.disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭
|
||||
*/
|
||||
@@ -166,7 +230,7 @@ object PTClient {
|
||||
map["timestamp"] = sdf.format(Date())
|
||||
map["serialNo"] = ptDate()
|
||||
map["sign"] = RSAUtil.sign(RSAUtil.getSignatureContent(map), Global.ptPrivateKey) ?: ""
|
||||
return myJson.encodeToString(map)
|
||||
return wireJson.encodeToString(map)
|
||||
}
|
||||
|
||||
fun disposeResponse(
|
||||
@@ -229,4 +293,4 @@ object PTClient {
|
||||
return str
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user