完成基础信息与开票的逻辑
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description:
|
||||
alwaysApply: true
|
||||
enabled: true
|
||||
updatedAt: 2026-05-08T02:44:45.758Z
|
||||
provider:
|
||||
---
|
||||
|
||||
1. 编码时要参考项目类似结构的代码,比如写接口要参考其他接口,写service要参考其他service,写dao要参考其他dao。
|
||||
2. 要保持代码干净、已读。
|
||||
3. 尽量不要出现重复代码,相同的逻辑需要提取出工具类到新文件中。
|
||||
@@ -0,0 +1,8 @@
|
||||
-- 扩展现有字段长度,适配票通接口校验规则和 base64 图片存储
|
||||
ALTER TABLE sys_user
|
||||
ALTER COLUMN tax_contact_name TYPE varchar(50),
|
||||
ALTER COLUMN tax_contact_email TYPE varchar(100),
|
||||
ALTER COLUMN tax_legal_person_name TYPE varchar(50),
|
||||
ALTER COLUMN tax_city_name TYPE varchar(50),
|
||||
ALTER COLUMN tax_enterprise_address TYPE varchar(200),
|
||||
ALTER COLUMN tax_registration_certificate TYPE text;
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.bbit.ticket.bootstrap
|
||||
|
||||
import com.bbit.ticket.database.piaotong.InvoiceItemTable
|
||||
import com.bbit.ticket.database.piaotong.InvoiceOrderTable
|
||||
import com.bbit.ticket.database.system.SysApiAccessLogTable
|
||||
import com.bbit.ticket.database.system.SysDictItemTable
|
||||
import com.bbit.ticket.database.system.SysDictTypeTable
|
||||
@@ -30,6 +32,8 @@ object DatabaseInitializer {
|
||||
SysDictItemTable,
|
||||
SysOperationLogTable,
|
||||
SysApiAccessLogTable,
|
||||
InvoiceItemTable,
|
||||
InvoiceOrderTable,
|
||||
)
|
||||
// 先通过 Exposed 生成迁移 SQL,再逐条执行,避免启动时静默跳过缺失表或字段。
|
||||
transaction {
|
||||
@@ -40,7 +44,7 @@ object DatabaseInitializer {
|
||||
if (statements.isNotEmpty()) {
|
||||
logger.info("Migrating database schema, statement count={}", statements.size)
|
||||
statements.forEach {
|
||||
logger.debug("Executing migration SQL: {};", it)
|
||||
logger.info("Executing migration SQL: {};", it)
|
||||
exec(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,24 @@ package com.bbit.ticket.bootstrap
|
||||
|
||||
object Global {
|
||||
|
||||
// 测试账号 销售方税号
|
||||
const val testTaxpayerNum = "500102201007206608"
|
||||
const val testAccount = "DEMOadmin"
|
||||
|
||||
val isDev = true
|
||||
|
||||
// 请求基础地址
|
||||
var baseUrl: String
|
||||
|
||||
// 票通私钥
|
||||
// 票通私钥 私钥(与发给票通的公钥为一对)
|
||||
var ptPrivateKey: String
|
||||
|
||||
// 票通公钥
|
||||
// 票通公钥 (票通提供)
|
||||
var ptPublicKey: String
|
||||
|
||||
// 票通密码
|
||||
// 票通密码 (票通提供)
|
||||
var ptPassword: String
|
||||
|
||||
// 票通平台简称
|
||||
// 票通平台简称 (票通提供)
|
||||
var ptPlatformAlias: String
|
||||
|
||||
// 票通编码
|
||||
// 票通编码 (票通提供)
|
||||
var ptPlatformCode: String
|
||||
|
||||
|
||||
|
||||
@@ -2,16 +2,10 @@
|
||||
|
||||
package com.bbit.ticket.bootstrap
|
||||
|
||||
import com.bbit.ticket.database.system.SysDictItemTable
|
||||
import com.bbit.ticket.database.system.SysDictTypeTable
|
||||
import com.bbit.ticket.database.system.SysMenuTable
|
||||
import com.bbit.ticket.database.system.SysOrgTable
|
||||
import com.bbit.ticket.database.system.SysRoleMenuTable
|
||||
import com.bbit.ticket.database.system.SysRoleTable
|
||||
import com.bbit.ticket.database.system.SysUserRoleTable
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.database.system.*
|
||||
import com.bbit.ticket.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
|
||||
@@ -34,6 +28,10 @@ object SeedData {
|
||||
private const val DEFAULT_ORG_CODE = "DEFAULT_ORG"
|
||||
private const val SUPER_ADMIN_ROLE_CODE = "SUPER_ADMIN"
|
||||
|
||||
// =========================================================
|
||||
// Main entry
|
||||
// =========================================================
|
||||
|
||||
suspend fun seed() {
|
||||
val now = OffsetDateTime.now()
|
||||
val orgId = upsertDefaultOrg(now)
|
||||
@@ -46,6 +44,10 @@ object SeedData {
|
||||
logger.info("Seed data initialized, default admin username: {}", ADMIN_USERNAME)
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Organization & Role
|
||||
// =========================================================
|
||||
|
||||
private suspend fun upsertDefaultOrg(now: OffsetDateTime): Uuid = dbQuery {
|
||||
val existing = SysOrgTable.selectAll()
|
||||
.where { (SysOrgTable.code eq DEFAULT_ORG_CODE) and SysOrgTable.deletedAt.isNull() }
|
||||
@@ -54,21 +56,21 @@ object SeedData {
|
||||
if (existing != null) {
|
||||
val id = existing[SysOrgTable.id]
|
||||
SysOrgTable.update({ SysOrgTable.id eq id }) {
|
||||
it[name] = "默认组织"
|
||||
it[sort] = 0
|
||||
it[status] = "ENABLED"
|
||||
it[updatedAt] = now
|
||||
it[SysOrgTable.name] = "默认组织"
|
||||
it[SysOrgTable.sort] = 0
|
||||
it[SysOrgTable.status] = "ENABLED"
|
||||
it[SysOrgTable.updatedAt] = now
|
||||
}
|
||||
return@dbQuery id
|
||||
}
|
||||
|
||||
val inserted = SysOrgTable.insert {
|
||||
it[parentId] = null
|
||||
it[name] = "默认组织"
|
||||
it[code] = DEFAULT_ORG_CODE
|
||||
it[sort] = 0
|
||||
it[status] = "ENABLED"
|
||||
it[createdAt] = now
|
||||
it[SysOrgTable.parentId] = null
|
||||
it[SysOrgTable.name] = "默认组织"
|
||||
it[SysOrgTable.code] = DEFAULT_ORG_CODE
|
||||
it[SysOrgTable.sort] = 0
|
||||
it[SysOrgTable.status] = "ENABLED"
|
||||
it[SysOrgTable.createdAt] = now
|
||||
}
|
||||
inserted[SysOrgTable.id]
|
||||
}
|
||||
@@ -81,26 +83,30 @@ object SeedData {
|
||||
if (existing != null) {
|
||||
val id = existing[SysRoleTable.id]
|
||||
SysRoleTable.update({ SysRoleTable.id eq id }) {
|
||||
it[name] = "超级管理员"
|
||||
it[description] = "系统内置超级管理员角色"
|
||||
it[status] = "ENABLED"
|
||||
it[dataScope] = "ALL"
|
||||
it[updatedAt] = now
|
||||
it[SysRoleTable.name] = "超级管理员"
|
||||
it[SysRoleTable.description] = "系统内置超级管理员角色"
|
||||
it[SysRoleTable.status] = "ENABLED"
|
||||
it[SysRoleTable.dataScope] = "ALL"
|
||||
it[SysRoleTable.updatedAt] = now
|
||||
}
|
||||
return@dbQuery id
|
||||
}
|
||||
|
||||
val inserted = SysRoleTable.insert {
|
||||
it[name] = "超级管理员"
|
||||
it[code] = SUPER_ADMIN_ROLE_CODE
|
||||
it[description] = "系统内置超级管理员角色"
|
||||
it[status] = "ENABLED"
|
||||
it[dataScope] = "ALL"
|
||||
it[createdAt] = now
|
||||
it[SysRoleTable.name] = "超级管理员"
|
||||
it[SysRoleTable.code] = SUPER_ADMIN_ROLE_CODE
|
||||
it[SysRoleTable.description] = "系统内置超级管理员角色"
|
||||
it[SysRoleTable.status] = "ENABLED"
|
||||
it[SysRoleTable.dataScope] = "ALL"
|
||||
it[SysRoleTable.createdAt] = now
|
||||
}
|
||||
inserted[SysRoleTable.id]
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Admin user
|
||||
// =========================================================
|
||||
|
||||
private suspend fun upsertAdminUser(orgId: Uuid, now: OffsetDateTime): Uuid = dbQuery {
|
||||
val existing = SysUserTable.selectAll()
|
||||
.where { (SysUserTable.username eq ADMIN_USERNAME) and SysUserTable.deletedAt.isNull() }
|
||||
@@ -109,29 +115,34 @@ object SeedData {
|
||||
if (existing != null) {
|
||||
val id = existing[SysUserTable.id]
|
||||
SysUserTable.update({ SysUserTable.id eq id }) {
|
||||
it[nickname] = "管理员"
|
||||
it[realName] = "系统管理员"
|
||||
it[SysUserTable.nickname] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[status] = "ENABLED"
|
||||
it[updatedAt] = now
|
||||
it[taxpayerNum] = "500102201007206608"
|
||||
it[account] = "DEMOadmin"
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
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
|
||||
} else {
|
||||
val inserted = SysUserTable.insert {
|
||||
it[username] = ADMIN_USERNAME
|
||||
it[passwordHash] = PasswordService.hash(ADMIN_INIT_PASSWORD)
|
||||
it[nickname] = "管理员"
|
||||
it[realName] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[status] = "ENABLED"
|
||||
it[tokenVersion] = 1
|
||||
it[taxpayerNum] = "500102201007206608"
|
||||
it[account] = "DEMOadmin"
|
||||
}
|
||||
inserted[SysUserTable.id]
|
||||
}
|
||||
|
||||
val inserted = SysUserTable.insert {
|
||||
it[SysUserTable.username] = ADMIN_USERNAME
|
||||
it[SysUserTable.passwordHash] = PasswordService.hash(ADMIN_INIT_PASSWORD)
|
||||
it[SysUserTable.nickname] = "系统管理员"
|
||||
it[SysUserTable.orgId] = orgId
|
||||
it[SysUserTable.status] = "ENABLED"
|
||||
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] = "测试"
|
||||
}
|
||||
inserted[SysUserTable.id]
|
||||
}
|
||||
|
||||
private suspend fun upsertUserRole(userId: Uuid, roleId: Uuid) = dbQuery {
|
||||
@@ -146,359 +157,42 @@ object SeedData {
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Menus & permissions
|
||||
// =========================================================
|
||||
|
||||
private suspend fun upsertMenus(now: OffsetDateTime): List<Uuid> {
|
||||
val seedMenus = listOf(
|
||||
SeedMenu(
|
||||
"dashboard",
|
||||
null,
|
||||
"MENU",
|
||||
"工作台",
|
||||
"Dashboard",
|
||||
"/dashboard",
|
||||
"dashboard/index",
|
||||
"LayoutDashboard",
|
||||
null,
|
||||
10,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"system",
|
||||
null,
|
||||
"CATALOG",
|
||||
"系统管理",
|
||||
"SystemRoot",
|
||||
"/system",
|
||||
null,
|
||||
"Settings",
|
||||
null,
|
||||
20,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_user",
|
||||
"system",
|
||||
"MENU",
|
||||
"用户管理",
|
||||
"SystemUsers",
|
||||
"/system/users",
|
||||
"system/users/index",
|
||||
"Users",
|
||||
"system:user:view",
|
||||
10,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"system_user_create",
|
||||
"system_user",
|
||||
"BUTTON",
|
||||
"新增用户",
|
||||
"SystemUserCreate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:user:create",
|
||||
1,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_user_update",
|
||||
"system_user",
|
||||
"BUTTON",
|
||||
"修改用户",
|
||||
"SystemUserUpdate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:user:update",
|
||||
2,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_user_delete",
|
||||
"system_user",
|
||||
"BUTTON",
|
||||
"删除用户",
|
||||
"SystemUserDelete",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:user:delete",
|
||||
3,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_org",
|
||||
"system",
|
||||
"MENU",
|
||||
"组织管理",
|
||||
"SystemOrgs",
|
||||
"/system/orgs",
|
||||
"system/orgs/index",
|
||||
"Building2",
|
||||
"system:org:view",
|
||||
20,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"system_org_create",
|
||||
"system_org",
|
||||
"BUTTON",
|
||||
"新增组织",
|
||||
"SystemOrgCreate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:org:create",
|
||||
1,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_org_update",
|
||||
"system_org",
|
||||
"BUTTON",
|
||||
"更新组织",
|
||||
"SystemOrgUpdate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:org:update",
|
||||
2,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_org_delete",
|
||||
"system_org",
|
||||
"BUTTON",
|
||||
"删除组织",
|
||||
"SystemOrgDelete",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:org:delete",
|
||||
3,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_role",
|
||||
"system",
|
||||
"MENU",
|
||||
"角色管理",
|
||||
"SystemRoles",
|
||||
"/system/roles",
|
||||
"system/roles/index",
|
||||
"Shield",
|
||||
"system:role:view",
|
||||
30,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"system_role_create",
|
||||
"system_role",
|
||||
"BUTTON",
|
||||
"新增角色",
|
||||
"SystemRoleCreate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:role:create",
|
||||
1,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_role_update",
|
||||
"system_role",
|
||||
"BUTTON",
|
||||
"更新角色",
|
||||
"SystemRoleUpdate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:role:update",
|
||||
2,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_role_delete",
|
||||
"system_role",
|
||||
"BUTTON",
|
||||
"删除角色",
|
||||
"SystemRoleDelete",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:role:delete",
|
||||
3,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_role_assign",
|
||||
"system_role",
|
||||
"BUTTON",
|
||||
"分配角色权限",
|
||||
"SystemRoleAssign",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:role:assign",
|
||||
4,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_menu",
|
||||
"system",
|
||||
"MENU",
|
||||
"菜单管理",
|
||||
"SystemMenus",
|
||||
"/system/menus",
|
||||
"system/menus/index",
|
||||
"PanelLeft",
|
||||
"system:menu:view",
|
||||
40,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"system_menu_create",
|
||||
"system_menu",
|
||||
"BUTTON",
|
||||
"新增菜单",
|
||||
"SystemMenuCreate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:menu:create",
|
||||
1,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_menu_update",
|
||||
"system_menu",
|
||||
"BUTTON",
|
||||
"更新菜单",
|
||||
"SystemMenuUpdate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:menu:update",
|
||||
2,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_menu_delete",
|
||||
"system_menu",
|
||||
"BUTTON",
|
||||
"删除菜单",
|
||||
"SystemMenuDelete",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:menu:delete",
|
||||
3,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_dict",
|
||||
"system",
|
||||
"MENU",
|
||||
"字典管理",
|
||||
"SystemDict",
|
||||
"/system/dicts",
|
||||
"system/dicts/index",
|
||||
"BookType",
|
||||
"system:dict:view",
|
||||
50,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"system_dict_create",
|
||||
"system_dict",
|
||||
"BUTTON",
|
||||
"新增字典",
|
||||
"SystemDictCreate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:dict:create",
|
||||
1,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_dict_update",
|
||||
"system_dict",
|
||||
"BUTTON",
|
||||
"更新字典",
|
||||
"SystemDictUpdate",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:dict:update",
|
||||
2,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu(
|
||||
"system_dict_delete",
|
||||
"system_dict",
|
||||
"BUTTON",
|
||||
"删除字典",
|
||||
"SystemDictDelete",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"system:dict:delete",
|
||||
3,
|
||||
true,
|
||||
false
|
||||
),
|
||||
SeedMenu("logs", null, "CATALOG", "日志管理", "LogsRoot", "/logs", null, "Logs", null, 30, true, false),
|
||||
SeedMenu(
|
||||
"logs_operation",
|
||||
"logs",
|
||||
"MENU",
|
||||
"操作日志",
|
||||
"LogsOperation",
|
||||
"/logs/operation",
|
||||
"logs/operation/index",
|
||||
"ScrollText",
|
||||
"log:operation:view",
|
||||
10,
|
||||
true,
|
||||
true
|
||||
),
|
||||
SeedMenu(
|
||||
"logs_api_access",
|
||||
"logs",
|
||||
"MENU",
|
||||
"接口日志",
|
||||
"LogsApiAccess",
|
||||
"/logs/api-access",
|
||||
"logs/api-access/index",
|
||||
"Waypoints",
|
||||
"log:api-access:view",
|
||||
20,
|
||||
true,
|
||||
true
|
||||
),
|
||||
rootMenu("dashboard", "工作台", "Dashboard", "/dashboard", "dashboard/index", "LayoutDashboard", 10),
|
||||
catalog("system", "系统管理", "SystemRoot", "Settings", 20),
|
||||
subMenu("system_user", "system", "用户管理", "SystemUsers", "/system/users", "system/users/index", "Users", "system:user:view", 10),
|
||||
button("system_user_create", "system_user", "新增用户", "SystemUserCreate", "system:user:create", 1),
|
||||
button("system_user_update", "system_user", "修改用户", "SystemUserUpdate", "system:user:update", 2),
|
||||
button("system_user_delete", "system_user", "删除用户", "SystemUserDelete", "system:user:delete", 3),
|
||||
subMenu("system_org", "system", "组织管理", "SystemOrgs", "/system/orgs", "system/orgs/index", "Building2", "system:org:view", 20),
|
||||
button("system_org_create", "system_org", "新增组织", "SystemOrgCreate", "system:org:create", 1),
|
||||
button("system_org_update", "system_org", "更新组织", "SystemOrgUpdate", "system:org:update", 2),
|
||||
button("system_org_delete", "system_org", "删除组织", "SystemOrgDelete", "system:org:delete", 3),
|
||||
subMenu("system_role", "system", "角色管理", "SystemRoles", "/system/roles", "system/roles/index", "Shield", "system:role:view", 30),
|
||||
button("system_role_create", "system_role", "新增角色", "SystemRoleCreate", "system:role:create", 1),
|
||||
button("system_role_update", "system_role", "更新角色", "SystemRoleUpdate", "system:role:update", 2),
|
||||
button("system_role_delete", "system_role", "删除角色", "SystemRoleDelete", "system:role:delete", 3),
|
||||
button("system_role_assign", "system_role", "分配角色权限", "SystemRoleAssign", "system:role:assign", 4),
|
||||
subMenu("system_menu", "system", "菜单管理", "SystemMenus", "/system/menus", "system/menus/index", "PanelLeft", "system:menu:view", 40),
|
||||
button("system_menu_create", "system_menu", "新增菜单", "SystemMenuCreate", "system:menu:create", 1),
|
||||
button("system_menu_update", "system_menu", "更新菜单", "SystemMenuUpdate", "system:menu:update", 2),
|
||||
button("system_menu_delete", "system_menu", "删除菜单", "SystemMenuDelete", "system:menu:delete", 3),
|
||||
subMenu("system_dict", "system", "字典管理", "SystemDict", "/system/dicts", "system/dicts/index", "BookType", "system:dict:view", 50),
|
||||
button("system_dict_create", "system_dict", "新增字典", "SystemDictCreate", "system:dict:create", 1),
|
||||
button("system_dict_update", "system_dict", "更新字典", "SystemDictUpdate", "system:dict:update", 2),
|
||||
button("system_dict_delete", "system_dict", "删除字典", "SystemDictDelete", "system:dict:delete", 3),
|
||||
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),
|
||||
)
|
||||
|
||||
val idMap = mutableMapOf<String, Uuid>()
|
||||
@@ -507,7 +201,6 @@ object SeedData {
|
||||
val menuId = upsertMenu(menu, parentId, now)
|
||||
idMap[menu.key] = menuId
|
||||
}
|
||||
|
||||
return idMap.values.toList()
|
||||
}
|
||||
|
||||
@@ -558,16 +251,15 @@ object SeedData {
|
||||
}
|
||||
|
||||
private suspend fun bindRoleMenus(roleId: Uuid, menuIds: List<Uuid>) = dbQuery {
|
||||
if (menuIds.isEmpty()) {
|
||||
return@dbQuery
|
||||
}
|
||||
if (menuIds.isEmpty()) return@dbQuery
|
||||
|
||||
val existing = SysRoleMenuTable.selectAll()
|
||||
.where { SysRoleMenuTable.roleId eq roleId }
|
||||
.map { it[SysRoleMenuTable.menuId] }
|
||||
.toSet()
|
||||
|
||||
val toAdd = menuIds.filter { !existing.contains(it) }
|
||||
val menuIdSet = menuIds.toSet()
|
||||
val toAdd = menuIds.filter { it !in existing }
|
||||
toAdd.forEach { menuId ->
|
||||
SysRoleMenuTable.insert {
|
||||
it[SysRoleMenuTable.roleId] = roleId
|
||||
@@ -575,33 +267,45 @@ object SeedData {
|
||||
}
|
||||
}
|
||||
|
||||
val toRemove = existing.filter { !menuIds.contains(it) }
|
||||
val toRemove = existing - menuIdSet
|
||||
if (toRemove.isNotEmpty()) {
|
||||
SysRoleMenuTable.deleteWhere { (SysRoleMenuTable.roleId eq roleId) and (SysRoleMenuTable.menuId inList toRemove) }
|
||||
SysRoleMenuTable.deleteWhere {
|
||||
(SysRoleMenuTable.roleId eq roleId) and (SysRoleMenuTable.menuId inList toRemove.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Dicts
|
||||
// =========================================================
|
||||
|
||||
private data class SeedDict(val code: String, val name: String, val items: List<SeedDictItem>)
|
||||
private data class SeedDictItem(val label: String, val value: String, val color: String?, val sort: Int)
|
||||
|
||||
private suspend fun seedDicts(now: OffsetDateTime) {
|
||||
val userStatusTypeId = upsertDictType("user_status", "用户状态", now)
|
||||
upsertDictItem(userStatusTypeId, "启用", "ENABLED", "green", 1, now)
|
||||
upsertDictItem(userStatusTypeId, "禁用", "DISABLED", "red", 2, now)
|
||||
val statusItems = listOf(
|
||||
SeedDictItem("启用", "ENABLED", "green", 1),
|
||||
SeedDictItem("禁用", "DISABLED", "red", 2),
|
||||
)
|
||||
|
||||
val orgStatusTypeId = upsertDictType("org_status", "组织状态", now)
|
||||
upsertDictItem(orgStatusTypeId, "启用", "ENABLED", "green", 1, now)
|
||||
upsertDictItem(orgStatusTypeId, "禁用", "DISABLED", "red", 2, now)
|
||||
val dicts = listOf(
|
||||
SeedDict("user_status", "用户状态", statusItems),
|
||||
SeedDict("org_status", "组织状态", statusItems),
|
||||
SeedDict("role_status", "角色状态", statusItems),
|
||||
SeedDict("menu_type", "菜单类型", listOf(
|
||||
SeedDictItem("目录", "CATALOG", "default", 1),
|
||||
SeedDictItem("菜单", "MENU", "blue", 2),
|
||||
SeedDictItem("按钮", "BUTTON", "orange", 3),
|
||||
)),
|
||||
SeedDict("log_status", "日志状态", statusItems),
|
||||
)
|
||||
|
||||
val roleStatusTypeId = upsertDictType("role_status", "角色状态", now)
|
||||
upsertDictItem(roleStatusTypeId, "启用", "ENABLED", "green", 1, now)
|
||||
upsertDictItem(roleStatusTypeId, "禁用", "DISABLED", "red", 2, now)
|
||||
|
||||
val menuTypeId = upsertDictType("menu_type", "菜单类型", now)
|
||||
upsertDictItem(menuTypeId, "目录", "CATALOG", "default", 1, now)
|
||||
upsertDictItem(menuTypeId, "菜单", "MENU", "blue", 2, now)
|
||||
upsertDictItem(menuTypeId, "按钮", "BUTTON", "orange", 3, now)
|
||||
|
||||
val logStatusTypeId = upsertDictType("log_status", "日志状态", now)
|
||||
upsertDictItem(logStatusTypeId, "成功", "SUCCESS", "green", 1, now)
|
||||
upsertDictItem(logStatusTypeId, "失败", "FAIL", "red", 2, now)
|
||||
for (dict in dicts) {
|
||||
val typeId = upsertDictType(dict.code, dict.name, now)
|
||||
for (item in dict.items) {
|
||||
upsertDictItem(typeId, item.label, item.value, item.color, item.sort, now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun upsertDictType(code: String, name: String, now: OffsetDateTime): Uuid = dbQuery {
|
||||
@@ -613,8 +317,8 @@ object SeedData {
|
||||
val id = existing[SysDictTypeTable.id]
|
||||
SysDictTypeTable.update({ SysDictTypeTable.id eq id }) {
|
||||
it[SysDictTypeTable.name] = name
|
||||
it[status] = "ENABLED"
|
||||
it[updatedAt] = now
|
||||
it[SysDictTypeTable.status] = "ENABLED"
|
||||
it[SysDictTypeTable.updatedAt] = now
|
||||
}
|
||||
return@dbQuery id
|
||||
}
|
||||
@@ -622,19 +326,14 @@ object SeedData {
|
||||
val inserted = SysDictTypeTable.insert {
|
||||
it[SysDictTypeTable.code] = code
|
||||
it[SysDictTypeTable.name] = name
|
||||
it[status] = "ENABLED"
|
||||
it[createdAt] = now
|
||||
it[SysDictTypeTable.status] = "ENABLED"
|
||||
it[SysDictTypeTable.createdAt] = now
|
||||
}
|
||||
inserted[SysDictTypeTable.id]
|
||||
}
|
||||
|
||||
private suspend fun upsertDictItem(
|
||||
typeId: Uuid,
|
||||
label: String,
|
||||
value: String,
|
||||
color: String?,
|
||||
sort: Int,
|
||||
now: OffsetDateTime,
|
||||
typeId: Uuid, label: String, value: String, color: String?, sort: Int, now: OffsetDateTime,
|
||||
) = dbQuery {
|
||||
val existing = SysDictItemTable.selectAll()
|
||||
.where {
|
||||
@@ -645,13 +344,12 @@ object SeedData {
|
||||
.singleOrNull()
|
||||
|
||||
if (existing != null) {
|
||||
val id = existing[SysDictItemTable.id]
|
||||
SysDictItemTable.update({ SysDictItemTable.id eq id }) {
|
||||
SysDictItemTable.update({ SysDictItemTable.id eq existing[SysDictItemTable.id] }) {
|
||||
it[SysDictItemTable.label] = label
|
||||
it[SysDictItemTable.color] = color
|
||||
it[SysDictItemTable.sort] = sort
|
||||
it[status] = "ENABLED"
|
||||
it[updatedAt] = now
|
||||
it[SysDictItemTable.status] = "ENABLED"
|
||||
it[SysDictItemTable.updatedAt] = now
|
||||
}
|
||||
return@dbQuery
|
||||
}
|
||||
@@ -662,18 +360,22 @@ object SeedData {
|
||||
it[SysDictItemTable.value] = value
|
||||
it[SysDictItemTable.color] = color
|
||||
it[SysDictItemTable.sort] = sort
|
||||
it[status] = "ENABLED"
|
||||
it[createdAt] = now
|
||||
it[SysDictItemTable.status] = "ENABLED"
|
||||
it[SysDictItemTable.createdAt] = now
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Menu definition helpers
|
||||
// =========================================================
|
||||
|
||||
private data class SeedMenu(
|
||||
val key: String,
|
||||
val parentKey: String?,
|
||||
val type: String,
|
||||
val title: String,
|
||||
val name: String,
|
||||
val name: String?,
|
||||
val path: String?,
|
||||
val component: String?,
|
||||
val icon: String?,
|
||||
@@ -683,3 +385,15 @@ private data class SeedMenu(
|
||||
val keepAlive: Boolean,
|
||||
val builtIn: Boolean = true,
|
||||
)
|
||||
|
||||
private fun rootMenu(key: String, title: String, name: String, path: String, component: String, icon: String, sort: Int): SeedMenu =
|
||||
SeedMenu(key, null, "MENU", title, name, path, component, icon, null, sort, visible = true, keepAlive = true)
|
||||
|
||||
private fun catalog(key: String, title: String, name: String, icon: String, sort: Int): SeedMenu =
|
||||
SeedMenu(key, null, "CATALOG", title, name, "/$key", null, icon, null, sort, visible = true, keepAlive = false)
|
||||
|
||||
private fun subMenu(key: String, parentKey: String, title: String, name: String, path: String, component: String, icon: String, permission: String, sort: Int): SeedMenu =
|
||||
SeedMenu(key, parentKey, "MENU", title, name, path, component, icon, permission, sort, visible = true, keepAlive = true)
|
||||
|
||||
private fun button(key: String, parentKey: String, title: String, name: String, permission: String, sort: Int): SeedMenu =
|
||||
SeedMenu(key, parentKey, "BUTTON", title, name, null, null, null, permission, sort, visible = true, keepAlive = false)
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
|
||||
package com.bbit.ticket.dao.piaotong
|
||||
|
||||
import com.bbit.ticket.database.piaotong.InvoiceItemTable
|
||||
import com.bbit.ticket.database.piaotong.InvoiceOrderTable
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.entity.request.InvoiceRequest
|
||||
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.PresetDataResponse
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.jdbc.insert
|
||||
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
|
||||
import org.jetbrains.exposed.v1.jdbc.insertReturning
|
||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||
import org.jetbrains.exposed.v1.jdbc.update
|
||||
import java.math.BigDecimal
|
||||
import java.text.DecimalFormat
|
||||
import java.time.OffsetDateTime
|
||||
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]
|
||||
)
|
||||
}
|
||||
|
||||
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.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()
|
||||
}
|
||||
}
|
||||
|
||||
fun addInvoice(userId: Uuid, req: InvoiceRequest) {
|
||||
val now = OffsetDateTime.now()
|
||||
val row = InvoiceOrderTable.insert {
|
||||
it[InvoiceOrderTable.userId] = userId
|
||||
it[InvoiceOrderTable.invoiceReqSerialNo] = req.invoiceReqSerialNo
|
||||
it[InvoiceOrderTable.taxpayerNum] = req.taxpayerNum
|
||||
it[InvoiceOrderTable.invoiceKindCode] = req.invoiceIssueKindCode
|
||||
it[InvoiceOrderTable.buyerName] = req.buyerName
|
||||
it[InvoiceOrderTable.buyerTaxpayerNum] = req.buyerTaxpayerNum
|
||||
it[InvoiceOrderTable.buyerAddress] = req.buyerAddress
|
||||
it[InvoiceOrderTable.buyerTel] = req.buyerTel
|
||||
it[InvoiceOrderTable.buyerBankName] = req.buyerBankName
|
||||
it[InvoiceOrderTable.buyerBankAccount] = req.buyerBankAccount
|
||||
it[InvoiceOrderTable.remark] = req.remark
|
||||
it[InvoiceOrderTable.definedData] = req.definedData
|
||||
it[InvoiceOrderTable.tradeNo] = req.tradeNo
|
||||
it[InvoiceOrderTable.taxAmount] = BigDecimal.ZERO
|
||||
it[InvoiceOrderTable.amount] = BigDecimal.ZERO
|
||||
it[InvoiceOrderTable.totalAmount] = BigDecimal.ZERO
|
||||
it[InvoiceOrderTable.requestJson] = Json.encodeToString(req)
|
||||
it[InvoiceOrderTable.status] = "PENDING"
|
||||
it[InvoiceOrderTable.createdAt] = now
|
||||
it[InvoiceOrderTable.createdBy] = userId
|
||||
}
|
||||
val invoiceId = row[InvoiceOrderTable.id]
|
||||
var lineNo = 1
|
||||
for (item in req.itemList) {
|
||||
InvoiceItemTable.insert {
|
||||
it[InvoiceItemTable.invoiceId] = invoiceId
|
||||
it[InvoiceItemTable.lineNo] = lineNo++
|
||||
it[InvoiceItemTable.goodsName] = item.goodsName
|
||||
it[InvoiceItemTable.taxClassificationCode] = item.taxClassificationCode
|
||||
it[InvoiceItemTable.specificationModel] = item.specificationModel
|
||||
it[InvoiceItemTable.meteringUnit] = item.meteringUnit
|
||||
it[InvoiceItemTable.quantity] = item.quantity?.toBigDecimalOrNull()
|
||||
it[InvoiceItemTable.unitPrice] = item.unitPrice?.toBigDecimalOrNull()
|
||||
it[InvoiceItemTable.invoiceAmount] = item.invoiceAmount.toBigDecimal()
|
||||
it[InvoiceItemTable.taxRateValue] = item.taxRateValue.toBigDecimal()
|
||||
it[InvoiceItemTable.taxRateAmount] = item.taxRateAmount?.toBigDecimalOrNull()
|
||||
it[InvoiceItemTable.includeTaxFlag] = item.includeTaxFlag == "1"
|
||||
it[InvoiceItemTable.discountAmount] = item.discountAmount?.toBigDecimalOrNull()
|
||||
it[InvoiceItemTable.zeroTaxFlag] = item.zeroTaxFlag
|
||||
it[InvoiceItemTable.preferentialPolicyFlag] = item.preferentialPolicyFlag
|
||||
it[InvoiceItemTable.vatSpecialManage] = item.vatSpecialManage
|
||||
it[InvoiceItemTable.deductionAmount] = item.deductionAmount?.toBigDecimalOrNull()
|
||||
it[InvoiceItemTable.createdAt] = now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,10 @@ 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()
|
||||
}
|
||||
}
|
||||
@@ -231,5 +235,9 @@ object UserDao {
|
||||
status = this[SysUserTable.status],
|
||||
statusLabel = statusLabel(this[SysUserTable.status]),
|
||||
roleIds = roleIds,
|
||||
taxpayerNum = this[SysUserTable.taxpayerNum],
|
||||
account = this[SysUserTable.taxAccount],
|
||||
taxPassword = this[SysUserTable.taxPassword],
|
||||
taxIdentityType = this[SysUserTable.taxIdentityType],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
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 InvoiceItemTable : Table("invoice_item") {
|
||||
|
||||
val id = uuid("id").clientDefault { Uuid.Companion.random() }
|
||||
|
||||
/**
|
||||
* 发票ID
|
||||
*/
|
||||
val invoiceId = uuid("invoice_id").references(InvoiceOrderTable.id)
|
||||
|
||||
/**
|
||||
* 行号
|
||||
*/
|
||||
val lineNo = integer("line_no")
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
val goodsName = varchar("goods_name", 200)
|
||||
|
||||
/**
|
||||
* 税收分类编码
|
||||
*/
|
||||
val taxClassificationCode = varchar("tax_classification_code", 64)
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
val specificationModel =
|
||||
varchar("specification_model", 100).nullable()
|
||||
|
||||
/**
|
||||
* 单位
|
||||
*/
|
||||
val meteringUnit =
|
||||
varchar("metering_unit", 32).nullable()
|
||||
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
val quantity = decimal("quantity", 18, 8).nullable()
|
||||
|
||||
/**
|
||||
* 单价
|
||||
*/
|
||||
val unitPrice = decimal("unit_price", 18, 8).nullable()
|
||||
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
val invoiceAmount = decimal("invoice_amount", 18, 2)
|
||||
|
||||
/**
|
||||
* 税率
|
||||
*/
|
||||
val taxRateValue = decimal("tax_rate_value", 8, 4)
|
||||
|
||||
/**
|
||||
* 税额
|
||||
*/
|
||||
val taxRateAmount = decimal("tax_rate_amount", 18, 2).nullable()
|
||||
|
||||
/**
|
||||
* 是否含税
|
||||
*/
|
||||
val includeTaxFlag = bool("include_tax_flag").default(false)
|
||||
|
||||
/**
|
||||
* 折扣金额
|
||||
*/
|
||||
val discountAmount = decimal("discount_amount", 18, 2).nullable()
|
||||
|
||||
/**
|
||||
* 零税率标识
|
||||
*/
|
||||
val zeroTaxFlag = varchar("zero_tax_flag", 8).nullable()
|
||||
|
||||
/**
|
||||
* 优惠政策标识
|
||||
*/
|
||||
val preferentialPolicyFlag = varchar("preferential_policy_flag", 8).nullable()
|
||||
|
||||
/**
|
||||
* 增值税特殊管理
|
||||
*/
|
||||
val vatSpecialManage = varchar("vat_special_manage", 100).nullable()
|
||||
|
||||
/**
|
||||
* 差额扣除金额
|
||||
*/
|
||||
val deductionAmount = decimal("deduction_amount", 18, 2).nullable()
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
val createdAt = timestampWithTimeZone("created_at")
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
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 InvoiceOrderTable : Table("invoice_order") {
|
||||
|
||||
val id = uuid("id").clientDefault { Uuid.Companion.random() }
|
||||
|
||||
/**
|
||||
* 租户/组织ID
|
||||
*/
|
||||
val userId = uuid("user_id").nullable()
|
||||
|
||||
/**
|
||||
* 发票请求流水号
|
||||
*/
|
||||
val invoiceReqSerialNo = varchar("invoice_req_serial_no", 64)
|
||||
.uniqueIndex()
|
||||
|
||||
/**
|
||||
* 销方税号
|
||||
*/
|
||||
val taxpayerNum = varchar("taxpayer_num", 32)
|
||||
|
||||
/**
|
||||
* 发票种类
|
||||
* 81:电子发票(增值税专用发票)
|
||||
* 82:电子发票(普通发票)
|
||||
* 87:数电纸质发票(机动车销售统一发票)
|
||||
* 10:增值税电子普通发票
|
||||
* 08:增值税电子专用发票
|
||||
* 04:增值税普通发票
|
||||
* 01:增值税专用发票
|
||||
*
|
||||
*/
|
||||
val invoiceKindCode = varchar("invoice_kind_code", 8)
|
||||
|
||||
// =========================
|
||||
// 购买方信息(历史快照)
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 购买方名称
|
||||
*/
|
||||
val buyerName = varchar("buyer_name", 200)
|
||||
|
||||
/**
|
||||
* 购买方税号
|
||||
*/
|
||||
val buyerTaxpayerNum = varchar("buyer_taxpayer_num", 32)
|
||||
.nullable()
|
||||
|
||||
val buyerAddress = varchar("buyer_address", 255).nullable()
|
||||
|
||||
val buyerTel = varchar("buyer_tel", 50).nullable()
|
||||
|
||||
val buyerBankName = varchar("buyer_bank_name", 100).nullable()
|
||||
|
||||
val buyerBankAccount = varchar("buyer_bank_account", 100).nullable()
|
||||
|
||||
// =========================
|
||||
// 金额
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 不含税金额
|
||||
*/
|
||||
val amount = decimal("amount", 18, 2)
|
||||
|
||||
/**
|
||||
* 税额
|
||||
*/
|
||||
val taxAmount = decimal("tax_amount", 18, 2)
|
||||
|
||||
/**
|
||||
* 含税总金额
|
||||
*/
|
||||
val totalAmount = decimal("total_amount", 18, 2)
|
||||
|
||||
// =========================
|
||||
// 开票结果
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 发票号码
|
||||
*/
|
||||
val invoiceNo = varchar("invoice_no", 64).nullable()
|
||||
|
||||
/**
|
||||
* 发票代码
|
||||
*/
|
||||
val invoiceCode = varchar("invoice_code", 64).nullable()
|
||||
|
||||
/**
|
||||
* 数电票号码
|
||||
*/
|
||||
val electronicInvoiceNo = varchar("electronic_invoice_no", 64).nullable()
|
||||
|
||||
/**
|
||||
* 开票时间
|
||||
*/
|
||||
val issuedAt = timestampWithTimeZone("issued_at").nullable()
|
||||
|
||||
/**
|
||||
* 开票状态
|
||||
*/
|
||||
val status = varchar("status", 32) .default("PENDING")
|
||||
|
||||
/**
|
||||
* 第三方平台返回错误
|
||||
*/
|
||||
val errorMessage = text("error_message").nullable()
|
||||
|
||||
/**
|
||||
* 原始请求报文
|
||||
*/
|
||||
val requestJson = text("request_json").nullable()
|
||||
|
||||
/**
|
||||
* 原始响应报文
|
||||
*/
|
||||
val responseJson = text("response_json").nullable()
|
||||
|
||||
// =========================
|
||||
// 文件
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* PDF地址
|
||||
*/
|
||||
val pdfUrl = text("pdf_url").nullable()
|
||||
|
||||
/**
|
||||
* OFD地址
|
||||
*/
|
||||
val ofdUrl = text("ofd_url").nullable()
|
||||
|
||||
/**
|
||||
* XML地址
|
||||
*/
|
||||
val xmlUrl = text("xml_url").nullable()
|
||||
|
||||
// =========================
|
||||
// 业务字段
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
val tradeNo = varchar("trade_no", 128).nullable()
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
val remark = text("remark").nullable()
|
||||
|
||||
/**
|
||||
* 自定义透传数据
|
||||
*/
|
||||
val definedData = text("defined_data").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()
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
@@ -11,8 +11,6 @@ object SysUserTable : Table("sys_user") {
|
||||
val username = varchar("username", 50).uniqueIndex()
|
||||
val passwordHash = varchar("password_hash", 255)
|
||||
val nickname = varchar("nickname", 50).nullable()
|
||||
val realName = varchar("real_name", 50).nullable()
|
||||
val phone = varchar("phone", 32).nullable()
|
||||
val email = varchar("email", 100).nullable()
|
||||
val avatar = text("avatar").nullable()
|
||||
val orgId = uuid("org_id").nullable()
|
||||
@@ -28,8 +26,29 @@ object SysUserTable : Table("sys_user") {
|
||||
val deletedBy = uuid("deleted_by").nullable()
|
||||
val version = integer("version").default(1)
|
||||
|
||||
val taxpayerNum = varchar("taxpayer_num", 50).nullable()
|
||||
val account = varchar("account", 50).nullable()
|
||||
// 核心业务逻辑字段=================
|
||||
// 数电账号相关信息
|
||||
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() // 企业注册证书 证件图片base64
|
||||
|
||||
val bankName = varchar("bank_name", 100).nullable()
|
||||
val bankAccount = varchar("bank_account", 50).nullable()
|
||||
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,438 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* 数电发票开票请求
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceRequest(
|
||||
|
||||
/**
|
||||
* 销方纳税人识别号(销售方税号)
|
||||
* 长度15~20,只允许大写字母和数字
|
||||
*/
|
||||
val taxpayerNum: String,
|
||||
|
||||
/**
|
||||
* 发票请求流水号
|
||||
* 一般格式:4位平台简称 + 16位随机数
|
||||
* 必须唯一
|
||||
*/
|
||||
val invoiceReqSerialNo: String,
|
||||
|
||||
/**
|
||||
* 开票种类
|
||||
*
|
||||
* 81:数电专票
|
||||
* 82:数电普通发票
|
||||
* 10:增值税电子普通发票
|
||||
* 08:增值税电子专用发票
|
||||
*/
|
||||
val invoiceIssueKindCode: String = "82",
|
||||
|
||||
/**
|
||||
* 购买方名称(发票抬头)
|
||||
*/
|
||||
val buyerName: String,
|
||||
|
||||
/**
|
||||
* 农产品收购发票销售方证件类型
|
||||
*
|
||||
* 证件类型代码证件类型名称
|
||||
* 201居民身份证
|
||||
* 208外国护照
|
||||
* 210港澳居民来往内地通行证
|
||||
* 213台湾居民来往大陆通行证
|
||||
* 215外国人居留证
|
||||
* 219香港永久性居民身份证
|
||||
* 220台湾身份证
|
||||
* 221澳门特别行政区永久性居民身份证
|
||||
* 233外国人永久居留身份证(外国人永久居留证)
|
||||
* 103税务登记证
|
||||
* 299其他个人证件
|
||||
*/
|
||||
val purchaseInvSellerIdType: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方纳税人识别号
|
||||
* 个人开票时通常为空
|
||||
*/
|
||||
val buyerTaxpayerNum: String? = null,
|
||||
|
||||
/**
|
||||
* 是否开具给自然人
|
||||
*
|
||||
* 0:否
|
||||
* 1:是 电子税局勾选“是”时的提示:请您确认受票方为自然人,并在纳税人识别号档次填入“自然人纳税人识别号”
|
||||
* (自然人受票方可登录个人所得税APP查看“自然人纳税人识别号”),该张发票将在受票方自然人个人票夹中展示。
|
||||
*
|
||||
*/
|
||||
val naturalPersonFlag: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方地址
|
||||
*/
|
||||
val buyerAddress: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方电话
|
||||
*/
|
||||
val buyerTel: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方开户银行
|
||||
*/
|
||||
val buyerBankName: String? = null,
|
||||
|
||||
/**
|
||||
* 购买方银行账号
|
||||
*/
|
||||
val buyerBankAccount: String? = null,
|
||||
|
||||
/**
|
||||
* 销方地址
|
||||
* 不传时平台自动取默认配置
|
||||
*/
|
||||
val sellerAddress: String? = null,
|
||||
|
||||
/**
|
||||
* 销方电话
|
||||
*/
|
||||
val sellerTel: String? = null,
|
||||
|
||||
/**
|
||||
* 销方开户银行
|
||||
*/
|
||||
val sellerBankName: String? = null,
|
||||
|
||||
/**
|
||||
* 销方银行账号
|
||||
*/
|
||||
val sellerBankAccount: String? = null,
|
||||
|
||||
/**
|
||||
* 是否在备注中显示购买方银行信息
|
||||
*
|
||||
* 0:不显示
|
||||
* 1:显示
|
||||
*/
|
||||
val showBuyerBank: String? = null,
|
||||
|
||||
/**
|
||||
* 是否在备注中显示销售方开户行及账号到发票备注
|
||||
*
|
||||
* 0:不显示
|
||||
* 1:显示
|
||||
*/
|
||||
val showSellerBank: String? = null,
|
||||
|
||||
/**
|
||||
* 是否在备注中显示购买方地址电话
|
||||
*/
|
||||
val showBuyerAddrTel: String? = null,
|
||||
|
||||
/**
|
||||
* 是否在备注中显示销售方地址电话
|
||||
*/
|
||||
val showSellerAddrTel: String? = null,
|
||||
|
||||
/**
|
||||
* 开票员税局账号
|
||||
* 不传则平台随机选择已登记开票员
|
||||
*/
|
||||
val account: String? = null,
|
||||
|
||||
/**
|
||||
* 数电发票差额征税标识。只有差额征税时需要填写。
|
||||
*
|
||||
* 1:全额开票
|
||||
* 2:差额开票
|
||||
*/
|
||||
val variableLevyFlag: String? = null,
|
||||
|
||||
/**
|
||||
* 收款人名称
|
||||
*/
|
||||
val casherName: String? = null,
|
||||
|
||||
/**
|
||||
* 复核人名称
|
||||
*/
|
||||
val reviewerName: String? = null,
|
||||
|
||||
/**
|
||||
* 收票人姓名
|
||||
*/
|
||||
val takerName: String? = null,
|
||||
|
||||
/**
|
||||
* 收票人手机号
|
||||
*/
|
||||
val takerTel: String? = null,
|
||||
|
||||
/**
|
||||
* 收票人邮箱
|
||||
* 填写后系统可自动发送发票邮件
|
||||
*/
|
||||
val takerEmail: String? = null,
|
||||
|
||||
/**
|
||||
* 特殊票种
|
||||
*
|
||||
* 08:成品油发票
|
||||
* 02:农产品收购发票
|
||||
* 12:自产农产品销售发票
|
||||
*/
|
||||
val specialInvoiceKind: String? = null,
|
||||
|
||||
/**
|
||||
* 发票备注
|
||||
*/
|
||||
val remark: String? = null,
|
||||
|
||||
/**
|
||||
* 自定义数据
|
||||
* 回调推送时原样返回
|
||||
*/
|
||||
val definedData: String? = null,
|
||||
|
||||
/**
|
||||
* 业务订单号
|
||||
* 不传时默认使用 invoiceReqSerialNo
|
||||
*/
|
||||
val tradeNo: String? = null,
|
||||
|
||||
/**
|
||||
* 门店编号(集团版)
|
||||
*/
|
||||
val shopNum: String? = null,
|
||||
|
||||
/**
|
||||
* 发票商品明细
|
||||
*/
|
||||
val itemList: List<InvoiceItem>,
|
||||
|
||||
/**
|
||||
* 差额征税凭证明细
|
||||
*/
|
||||
val variableLevyProofList: List<VariableLevyProof>? = null,
|
||||
|
||||
/**
|
||||
* 订单列表(支持合并开票)
|
||||
*/
|
||||
val orderList: List<OrderInfo>? = null
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* 发票商品项目
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceItem(
|
||||
|
||||
/**
|
||||
* 商品/服务名称
|
||||
*/
|
||||
val goodsName: String,
|
||||
|
||||
/**
|
||||
* 税收分类编码
|
||||
* 使用国家统一税编
|
||||
*/
|
||||
val taxClassificationCode: String,
|
||||
|
||||
/**
|
||||
* 规格型号
|
||||
*/
|
||||
val specificationModel: String? = null,
|
||||
|
||||
/**
|
||||
* 计量单位
|
||||
*/
|
||||
val meteringUnit: String? = null,
|
||||
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
val quantity: String? = null,
|
||||
|
||||
/**
|
||||
* 含税标识
|
||||
*
|
||||
* 0:不含税
|
||||
* 1:含税
|
||||
*/
|
||||
val includeTaxFlag: String? = "0",
|
||||
|
||||
/**
|
||||
* 单价
|
||||
*/
|
||||
val unitPrice: String? = null,
|
||||
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
val invoiceAmount: String,
|
||||
|
||||
/**
|
||||
* 税率
|
||||
* 例如:
|
||||
* 0.13 = 13%
|
||||
*/
|
||||
val taxRateValue: String,
|
||||
|
||||
/**
|
||||
* 税额
|
||||
* 不传则系统自动计算
|
||||
*/
|
||||
val taxRateAmount: String? = null,
|
||||
|
||||
/**
|
||||
* 折扣金额(负数)
|
||||
*/
|
||||
val discountAmount: String? = null,
|
||||
|
||||
/**
|
||||
* 折扣税额
|
||||
*/
|
||||
val discountTaxRateAmount: String? = null,
|
||||
|
||||
/**
|
||||
* 差额征税扣除金额
|
||||
*/
|
||||
val deductionAmount: String? = null,
|
||||
|
||||
/**
|
||||
* 优惠政策标识
|
||||
*
|
||||
* 1:使用优惠政策
|
||||
*/
|
||||
val preferentialPolicyFlag: String? = null,
|
||||
|
||||
/**
|
||||
* 零税率标识
|
||||
*
|
||||
* 1:免税
|
||||
* 2:不征税
|
||||
* 3:普通零税率
|
||||
*/
|
||||
val zeroTaxFlag: String? = null,
|
||||
|
||||
/**
|
||||
* 增值税特殊管理说明
|
||||
*
|
||||
* 如:
|
||||
* 免税
|
||||
* 不征税
|
||||
* 简易征收
|
||||
*/
|
||||
val vatSpecialManage: String? = null,
|
||||
|
||||
/**
|
||||
* 指定开票员账号
|
||||
*/
|
||||
val account: String? = null
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* 差额征税凭证明细
|
||||
*/
|
||||
@Serializable
|
||||
data class VariableLevyProof(
|
||||
|
||||
/**
|
||||
* 凭证类型。
|
||||
* 01:数电票;
|
||||
* 02:增值税专用发票;
|
||||
* 03:增值税普通发票;
|
||||
* 04:营业税发票;
|
||||
* 05:财政票据;
|
||||
* 06:法院裁决书;
|
||||
* 07:契税完税凭证;
|
||||
* 08:其他发票类;
|
||||
* 09:其他扣除凭证。
|
||||
*/
|
||||
val proofType: String,
|
||||
|
||||
/**
|
||||
* 数电票号码。
|
||||
* 凭证类型为01数电票时必填。
|
||||
*/
|
||||
val electronicInvoiceNo: String? = null,
|
||||
|
||||
/**
|
||||
* 发票代码。
|
||||
* 凭证类型为02增值税专用发票时必填。
|
||||
* 凭证类型为03增值税普通发票时必填。
|
||||
* 凭证类型为04营业税发票时必填。
|
||||
*/
|
||||
val invoiceCode: String? = null,
|
||||
|
||||
/**
|
||||
* 发票号码。
|
||||
* 凭证类型为02增值税专用发票时必填。
|
||||
* 凭证类型为03增值税普通发票时必填。
|
||||
* 凭证类型为04营业税发票时必填。
|
||||
*/
|
||||
val invoiceNo: String? = null,
|
||||
|
||||
/**
|
||||
* 凭证号码
|
||||
*/
|
||||
val proofNo: String? = null,
|
||||
|
||||
/**
|
||||
* 开具日期
|
||||
* 开具日期。
|
||||
* 格式yyyy-MM-dd。
|
||||
* 凭证类型为01数电票时必填。
|
||||
* 凭证类型为02增值税专用发票时必填。
|
||||
* 凭证类型为03增值税普通发票时必填。
|
||||
* 凭证类型为04营业税发票时必填。
|
||||
*/
|
||||
val issueDate: String? = null,
|
||||
|
||||
/**
|
||||
* 凭证合计金额。
|
||||
* 不能等于0。
|
||||
*/
|
||||
val proofAmount: String,
|
||||
|
||||
/**
|
||||
* 本次扣除金额。
|
||||
* 不能等于0。所有凭证的扣除金额要等于itemList中的 deductionAmount。
|
||||
*/
|
||||
val deductionAmount: String,
|
||||
|
||||
/**
|
||||
* 备注。
|
||||
* 凭证类型为08其他发票类时必填。
|
||||
* 凭证类型为09其他扣除凭证时必填。
|
||||
*/
|
||||
val proofRemark: String? = null,
|
||||
|
||||
/**
|
||||
* 来源。默认手工录入。
|
||||
* 手工录入;
|
||||
* 勾选录入;
|
||||
* 模板录入。
|
||||
*/
|
||||
val source: String? = null
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* 订单列表
|
||||
* 合并订单开票可以使用该字段传值
|
||||
*/
|
||||
@Serializable
|
||||
data class OrderInfo(
|
||||
|
||||
/**
|
||||
* 业务单据号
|
||||
*/
|
||||
val orderNo: String
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TaxRegister(
|
||||
|
||||
/**
|
||||
* 销方税号
|
||||
*/
|
||||
val taxpayerNum: String,
|
||||
|
||||
/**
|
||||
* 登录方式
|
||||
* 1:用户名(居民身份证号码/手机号码/用户名)+密码
|
||||
*/
|
||||
val loginMethod: String = "1",
|
||||
|
||||
/**
|
||||
* 电子税局登录账号
|
||||
* (手机号或身份证号)
|
||||
*/
|
||||
val account: String,
|
||||
|
||||
/**
|
||||
* 电子税局登录密码
|
||||
* 需要 3DES 加密
|
||||
*/
|
||||
val password: String,
|
||||
|
||||
/**
|
||||
* 登录身份类型
|
||||
*
|
||||
* 01:法定代表人
|
||||
* 02:财务负责人
|
||||
* 03:办税员
|
||||
* 04:涉税服务人员
|
||||
* 05:管理员
|
||||
* 07:领票人
|
||||
* 09:开票员
|
||||
* 99:其他人员
|
||||
*/
|
||||
val identityType: String,
|
||||
|
||||
/**
|
||||
* 登录身份密码
|
||||
* 多数地区不需要
|
||||
*/
|
||||
val identityPwd: String? = null,
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
val phoneNum: String,
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*
|
||||
* 1:登记
|
||||
* 2:删除
|
||||
*/
|
||||
val operationType: String? = null
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TaxRegisterInfo(
|
||||
/** 纳税人识别号 / 税号 */
|
||||
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
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TaxRegisterUserRequest(
|
||||
val taxpayerNum: String,
|
||||
val taxAccount: String
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdateDigitalAccountRequest(
|
||||
/** 纳税人识别号 / 税号 */
|
||||
val taxpayerNum: String = "",
|
||||
/** 电子税局账号 */
|
||||
val taxAccount: String = ""
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
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 = ""
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.bbit.ticket.entity.request
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UpdatePresetDataRequest(
|
||||
/** 开户银行 */
|
||||
val bankName: String = "",
|
||||
/** 银行账号 */
|
||||
val bankAccount: String = ""
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
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?,
|
||||
/** 注册证书 base64 */
|
||||
val taxRegistrationCertificate: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class DigitalAccountResponse(
|
||||
/** 纳税人识别号 */
|
||||
val taxpayerNum: String?,
|
||||
/** 电子税局账号 */
|
||||
val taxAccount: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PresetDataResponse(
|
||||
/** 开户银行 */
|
||||
val bankName: String?,
|
||||
/** 银行账号 */
|
||||
val bankAccount: String?
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EnterpriseTaxInfo(
|
||||
// 纳税人识别号 / 税号(通常是企业在税务系统中的唯一标识)
|
||||
val taxpayerNum: String,
|
||||
|
||||
// 企业名称(工商注册名称)
|
||||
val enterpriseName: String
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EtaxRegisterResponse(
|
||||
|
||||
@SerialName("resultCode")
|
||||
val resultCode: String,
|
||||
|
||||
@SerialName("resultMsg")
|
||||
val resultMsg: String,
|
||||
|
||||
@SerialName("resultData")
|
||||
val resultData: String? = null,
|
||||
|
||||
@SerialName("qrcodePath")
|
||||
val qrcodePath: String? = null,
|
||||
|
||||
@SerialName("qrcodeImgUrl")
|
||||
val qrcodeImgUrl: String? = null,
|
||||
|
||||
@SerialName("failureTime")
|
||||
val failureTime: String? = null
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* 发票开具响应结果
|
||||
*/
|
||||
@Serializable
|
||||
data class InvoiceCreateResponse(
|
||||
|
||||
/**
|
||||
* 发票请求流水号
|
||||
*
|
||||
* 用于关联本次开票请求。
|
||||
* 通常格式:
|
||||
* 4位平台简称 + 16位随机数
|
||||
*/
|
||||
val invoiceReqSerialNo: String,
|
||||
|
||||
/**
|
||||
* 发票状态二维码访问地址
|
||||
*
|
||||
* 一般是 Base64 字符串或二维码链接内容。
|
||||
* 用户扫码后可查看电子发票状态。
|
||||
*
|
||||
* 电子发票场景必传。
|
||||
*/
|
||||
val qrCodePath: String? = null,
|
||||
|
||||
/**
|
||||
* 二维码图片 Base64 数据
|
||||
*
|
||||
* 二维码内容通常就是 qrCodePath。
|
||||
* 前端可直接转图片展示。
|
||||
*
|
||||
* 电子发票场景必传。
|
||||
*/
|
||||
val qrCode: String? = null
|
||||
)
|
||||
+68
-48
@@ -4,72 +4,92 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TaxBureauAccountAuthContent(
|
||||
|
||||
/**
|
||||
* 微信用户绑定状态
|
||||
* 0-未绑定
|
||||
* 1-已绑定
|
||||
*/
|
||||
val wechatUserBindStatus: String,
|
||||
|
||||
/**
|
||||
* 操作建议
|
||||
*/
|
||||
val operationProposed: String,
|
||||
|
||||
/**
|
||||
* 实名认证状态
|
||||
*/
|
||||
val authStatus: String,
|
||||
|
||||
/**
|
||||
* 登录认证状态
|
||||
*/
|
||||
val loginAuthStatus: String,
|
||||
|
||||
/**
|
||||
* 最后登录认证时间
|
||||
*/
|
||||
val lastLoginAuthTime: String,
|
||||
|
||||
/**
|
||||
* 最后风险认证时间
|
||||
*/
|
||||
val lastRiskAuthTime: String,
|
||||
|
||||
/**
|
||||
* 最后认证成功时间
|
||||
*/
|
||||
val lastAuthSuccTime: String,
|
||||
|
||||
/**
|
||||
* 身份类型
|
||||
* 01-法人
|
||||
*/
|
||||
val identityType: String,
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* 纳税人识别号
|
||||
* 销售方纳税人识别号 (15-20位)
|
||||
*/
|
||||
val taxpayerNum: String,
|
||||
|
||||
/**
|
||||
* 是否允许切换
|
||||
* 电子税局登录账号
|
||||
*/
|
||||
val account: 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,
|
||||
|
||||
/**
|
||||
* 登录认证状态
|
||||
* 0:未登录
|
||||
* 1:已登录
|
||||
*/
|
||||
val loginAuthStatus: String,
|
||||
|
||||
/**
|
||||
* 最新登录认证时间 (yyyy-MM-dd HH:mm:ss)
|
||||
*/
|
||||
val lastLoginAuthTime: String,
|
||||
|
||||
/**
|
||||
* 风险认证状态
|
||||
* 0:未认证
|
||||
* 1:已认证
|
||||
*/
|
||||
val riskAuthStatus: String,
|
||||
|
||||
/**
|
||||
* 电子税局账号
|
||||
* 最新风险认证时间 (yyyy-MM-dd HH:mm:ss)
|
||||
*/
|
||||
val account: String
|
||||
val lastRiskAuthTime: String
|
||||
)
|
||||
@@ -31,8 +31,15 @@ data class CurrentUserProfile(
|
||||
val username: String,
|
||||
val nickname: String? = null,
|
||||
val realName: String? = null,
|
||||
val phone: String? = null,
|
||||
val email: String? = null,
|
||||
val orgId: String? = null,
|
||||
val status: String,
|
||||
val createdAt: String? = null,
|
||||
val taxpayerNum: String? = null,
|
||||
val account: String? = null,
|
||||
val taxPassword: String? = null,
|
||||
val taxIdentityType: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -26,6 +26,10 @@ data class UserDetailResponse(
|
||||
val status: String,
|
||||
val statusLabel: String,
|
||||
val roleIds: List<String>,
|
||||
val taxpayerNum: String? = null,
|
||||
val account: String? = null,
|
||||
val taxPassword: String? = null,
|
||||
val taxIdentityType: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -49,6 +53,10 @@ 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
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
@file:OptIn(ExperimentalUuidApi::class)
|
||||
|
||||
package com.bbit.ticket.route.piaotong
|
||||
|
||||
import com.bbit.ticket.bootstrap.Global
|
||||
import com.bbit.ticket.entity.common.PTException
|
||||
import com.bbit.ticket.entity.common.fail
|
||||
import com.bbit.ticket.entity.common.ok
|
||||
import com.bbit.ticket.entity.common.BizException
|
||||
import com.bbit.ticket.entity.common.ErrorCode
|
||||
import com.bbit.ticket.entity.request.InvoiceRequest
|
||||
import com.bbit.ticket.entity.request.TaxBureauAuthReq
|
||||
import com.bbit.ticket.entity.response.TaxBureauAccountAuthContent
|
||||
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.service.piaotong.PTAuthService
|
||||
import com.bbit.ticket.service.piaotong.PTConfigService
|
||||
import com.bbit.ticket.utils.requireCurrentUser
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.auth.authenticate
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.post
|
||||
import io.ktor.server.routing.put
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
|
||||
fun Route.registerPTTestRoutes() {
|
||||
route("/pt") {
|
||||
@@ -22,8 +35,8 @@ fun Route.registerPTTestRoutes() {
|
||||
get("/info") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val taxpayerNum = currentUser.taxpayerNum ?: Global.testTaxpayerNum
|
||||
val account = currentUser.account ?: Global.testAccount
|
||||
val taxpayerNum = currentUser.taxPayerNum ?: ""
|
||||
val account = currentUser.taxAccount ?: ""
|
||||
val response = PTAuthService.getTaxBureauAccountAuthStatus(
|
||||
TaxBureauAuthReq(taxpayerNum, account)
|
||||
)
|
||||
@@ -38,6 +51,136 @@ fun Route.registerPTTestRoutes() {
|
||||
)
|
||||
}
|
||||
}
|
||||
post("/register") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val req = call.receive<TaxRegisterInfo>()
|
||||
val response = PTAuthService.registerEnterprise(req, currentUser.id)
|
||||
call.respond(ok(response))
|
||||
} catch (e: PTException) {
|
||||
call.respond(
|
||||
fail(
|
||||
code = e.code,
|
||||
message = e.message,
|
||||
traceId = e.serialNo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
post("/registerUser") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val req = call.receive<TaxRegisterUserRequest>()
|
||||
val response = PTAuthService.registerUserFromPayload(req, currentUser)
|
||||
call.respond(ok(response))
|
||||
} catch (e: PTException) {
|
||||
call.respond(
|
||||
fail(
|
||||
code = e.code,
|
||||
message = e.message,
|
||||
traceId = e.serialNo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
// =============================================
|
||||
// 基础信息配置(本地 CRUD)
|
||||
// =============================================
|
||||
|
||||
// 1. 企业信息
|
||||
get("/enterprise") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val response = PTConfigService.getEnterpriseInfo(currentUser.id)
|
||||
if (response == null) {
|
||||
call.respond(ok(emptyMap<String, String>()))
|
||||
} else {
|
||||
call.respond(ok(response))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "查询企业信息失败"))
|
||||
}
|
||||
}
|
||||
|
||||
put("/enterprise") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val req = call.receive<UpdateEnterpriseInfoRequest>()
|
||||
val response = PTConfigService.updateEnterpriseInfo(currentUser.id, req)
|
||||
call.respond(ok(response))
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "保存企业信息失败"))
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 登记数电账号
|
||||
get("/digital-account") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val response = PTConfigService.getDigitalAccount(currentUser.id)
|
||||
if (response == null) {
|
||||
call.respond(ok(emptyMap<String, String>()))
|
||||
} else {
|
||||
call.respond(ok(response))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "查询数电账号失败"))
|
||||
}
|
||||
}
|
||||
|
||||
put("/digital-account") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val req = call.receive<UpdateDigitalAccountRequest>()
|
||||
val response = PTConfigService.updateDigitalAccount(currentUser.id, req)
|
||||
call.respond(ok(response))
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "保存数电账号失败"))
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 开票预设数据
|
||||
get("/preset") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val response = PTConfigService.getPresetData(currentUser.id)
|
||||
if (response == null) {
|
||||
call.respond(ok(emptyMap<String, String>()))
|
||||
} else {
|
||||
call.respond(ok(response))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "查询预设数据失败"))
|
||||
}
|
||||
}
|
||||
|
||||
put("/preset") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val req = call.receive<UpdatePresetDataRequest>()
|
||||
val response = PTConfigService.updatePresetData(currentUser.id, req)
|
||||
call.respond(ok(response))
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "保存预设数据失败"))
|
||||
}
|
||||
}
|
||||
|
||||
post("/invoiceBlue") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val req = call.receive<InvoiceRequest>()
|
||||
val response = PTAuthService.invoiceBlue(req, currentUser.id)
|
||||
call.respond(ok(response))
|
||||
} catch (e: PTException) {
|
||||
call.respond(
|
||||
fail(
|
||||
code = e.code,
|
||||
message = e.message,
|
||||
traceId = e.serialNo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,89 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
package com.bbit.ticket.service.piaotong
|
||||
|
||||
import com.bbit.ticket.dao.piaotong.EnterpriseTaxDao
|
||||
import com.bbit.ticket.entity.request.InvoiceRequest
|
||||
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.EnterpriseTaxInfo
|
||||
import com.bbit.ticket.entity.response.EtaxRegisterResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceCreateResponse
|
||||
import com.bbit.ticket.entity.response.TaxBureauAccountAuthContent
|
||||
import com.bbit.ticket.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object PTAuthService {
|
||||
|
||||
/**
|
||||
* 查询数电账号认证状态
|
||||
* 此接口用来查询数电账号的认证状态,会返回当
|
||||
*
|
||||
* @param taxpayerNum 纳税人识别号
|
||||
* @param account 账号
|
||||
*/
|
||||
suspend fun getTaxBureauAccountAuthStatus(req : TaxBureauAuthReq): String {
|
||||
val res = PTClient.ptPost<TaxBureauAuthReq, TaxBureauAccountAuthContent>(
|
||||
suspend fun getTaxBureauAccountAuthStatus(req: TaxBureauAuthReq): TaxBureauAccountAuthContent {
|
||||
val res = PTClient.ptPost<TaxBureauAuthReq, TaxBureauAccountAuthContent>(
|
||||
url = "getTaxBureauAccountAuthStatus.pt",
|
||||
body = req
|
||||
)
|
||||
println("res = $res")
|
||||
return res.taxpayerNum
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记/删除 数电账号
|
||||
*/
|
||||
/**
|
||||
* 登记/删除 数电账号(前端弹窗传入 taxpayerNum + taxAccount)
|
||||
*/
|
||||
suspend fun registerUserFromPayload(req: TaxRegisterUserRequest, currentUser: CurrentUser): String {
|
||||
val res = PTClient.ptPost<TaxRegister, EtaxRegisterResponse>(
|
||||
url = "registerUser.pt",
|
||||
body = 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册企业(纳税人)
|
||||
* 将企业信息注册到票通平台
|
||||
*/
|
||||
suspend fun registerEnterprise(req: TaxRegisterInfo, userId: Uuid): String {
|
||||
PTClient.ptPost<TaxRegisterInfo, EnterpriseTaxInfo>(
|
||||
url = "register.pt",
|
||||
body = req
|
||||
)
|
||||
dbQuery { EnterpriseTaxDao.updateEnterpriseInfo(userId, req) }
|
||||
return "操作成功,企业状态为审核中(待审核)"
|
||||
}
|
||||
/**
|
||||
* 蓝票接口调用
|
||||
*/
|
||||
suspend fun invoiceBlue(req: InvoiceRequest, userId: Uuid): String {
|
||||
PTClient.ptPost<InvoiceRequest, InvoiceCreateResponse>(
|
||||
url = "invoiceBlue.pt",
|
||||
body = req
|
||||
)
|
||||
dbQuery { EnterpriseTaxDao.addInvoice(userId, req) }
|
||||
return "操作成功,企业状态为审核中(待审核)"
|
||||
}
|
||||
/**
|
||||
* 红票接口调用
|
||||
*/
|
||||
suspend fun invoiceRed(req: InvoiceRequest, userId: Uuid): String {
|
||||
PTClient.ptPost<InvoiceRequest, InvoiceCreateResponse>(
|
||||
url = "invoiceBlue.pt",
|
||||
body = req
|
||||
)
|
||||
dbQuery { EnterpriseTaxDao.addInvoice(userId, req) }
|
||||
return "操作成功,企业状态为审核中(待审核)"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
@file:OptIn(kotlin.uuid.ExperimentalUuidApi::class)
|
||||
|
||||
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.plugins.dbQuery
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
object PTConfigService {
|
||||
|
||||
// =============================================
|
||||
// 企业信息
|
||||
// =============================================
|
||||
|
||||
suspend fun getEnterpriseInfo(userId: Uuid): EnterpriseInfoResponse? = dbQuery {
|
||||
EnterpriseTaxDao.getEnterpriseInfo(userId)
|
||||
}
|
||||
|
||||
suspend fun updateEnterpriseInfo(userId: Uuid, req: UpdateEnterpriseInfoRequest): String {
|
||||
dbQuery { EnterpriseTaxDao.updateEnterpriseInfoLocal(userId, req) }
|
||||
return "企业信息保存成功"
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 登记数电账号
|
||||
// =============================================
|
||||
|
||||
suspend fun getDigitalAccount(userId: Uuid): DigitalAccountResponse? = dbQuery {
|
||||
EnterpriseTaxDao.getDigitalAccount(userId)
|
||||
}
|
||||
|
||||
suspend fun updateDigitalAccount(userId: Uuid, req: UpdateDigitalAccountRequest): String {
|
||||
dbQuery { EnterpriseTaxDao.updateDigitalAccountLocal(userId, req) }
|
||||
return "账号信息保存成功"
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 开票预设数据
|
||||
// =============================================
|
||||
|
||||
suspend fun getPresetData(userId: Uuid): PresetDataResponse? = dbQuery {
|
||||
EnterpriseTaxDao.getPresetData(userId)
|
||||
}
|
||||
|
||||
suspend fun updatePresetData(userId: Uuid, req: UpdatePresetDataRequest): String {
|
||||
dbQuery { EnterpriseTaxDao.updatePresetData(userId, req) }
|
||||
return "预设数据保存成功"
|
||||
}
|
||||
}
|
||||
@@ -70,8 +70,15 @@ object AuthService {
|
||||
username = userRow[SysUserTable.username],
|
||||
nickname = userRow[SysUserTable.nickname],
|
||||
realName = userRow[SysUserTable.realName],
|
||||
phone = userRow[SysUserTable.phone],
|
||||
email = userRow[SysUserTable.email],
|
||||
orgId = userRow[SysUserTable.orgId]?.toString(),
|
||||
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],
|
||||
),
|
||||
menus = menuTree,
|
||||
permissions = permissions,
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.bbit.ticket.database.system.SysRoleMenuTable
|
||||
import com.bbit.ticket.database.system.SysRoleTable
|
||||
import com.bbit.ticket.database.system.SysUserRoleTable
|
||||
import com.bbit.ticket.database.system.SysUserTable
|
||||
import com.bbit.ticket.database.system.SysUserTable.account
|
||||
import com.bbit.ticket.plugins.dbQuery
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.ApplicationCall
|
||||
@@ -32,8 +31,12 @@ data class CurrentUser(
|
||||
val tokenVersion: Int,
|
||||
val roleCodes: Set<String>,
|
||||
val permissions: Set<String>,
|
||||
val taxpayerNum: String?,
|
||||
val account: 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")
|
||||
@@ -138,8 +141,12 @@ suspend fun ApplicationCall.requireCurrentUser(): CurrentUser {
|
||||
tokenVersion = userRow[SysUserTable.tokenVersion],
|
||||
roleCodes = roleCodes,
|
||||
permissions = permissions,
|
||||
taxpayerNum = userRow[SysUserTable.taxpayerNum],
|
||||
account = userRow[SysUserTable.account],
|
||||
taxPayerNum = userRow[SysUserTable.taxpayerNum],
|
||||
taxAccount = userRow[SysUserTable.taxAccount],
|
||||
taxPassword = userRow[SysUserTable.taxPassword],
|
||||
taxIdentityType = userRow[SysUserTable.taxIdentityType],
|
||||
phone = userRow[SysUserTable.phone],
|
||||
realName = userRow[SysUserTable.realName],
|
||||
)
|
||||
|
||||
attributes.put(CurrentUserKey, currentUser)
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
@@ -25,6 +26,7 @@ object PTClient {
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
explicitNulls = false
|
||||
ignoreUnknownKeys = true
|
||||
prettyPrint = true
|
||||
isLenient = true
|
||||
@@ -82,18 +84,12 @@ object PTClient {
|
||||
|
||||
val response = client.post(Global.baseUrl + url) {
|
||||
contentType(ContentType.Application.Json)
|
||||
|
||||
headers.forEach { (k, v) ->
|
||||
header(k, v)
|
||||
}
|
||||
|
||||
headers.forEach { (k, v) -> header(k, v) }
|
||||
setBody(buildRequestData(Json.encodeToString(body)))
|
||||
}.bodyAsText()
|
||||
|
||||
val decrypted = disposeResponse(response)
|
||||
|
||||
val result = Json.decodeFromString<PTResponse<JsonElement>>(decrypted)
|
||||
|
||||
if (result.code != "0000") {
|
||||
throw PTException(
|
||||
code = result.code,
|
||||
@@ -102,7 +98,8 @@ object PTClient {
|
||||
)
|
||||
}
|
||||
|
||||
return Json.decodeFromJsonElement(result.content!!)
|
||||
println("res = $result.content")
|
||||
return Json.decodeFromJsonElement<Resp>(result.content!!)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +121,7 @@ object PTClient {
|
||||
map["version"] = "1.0"
|
||||
map["content"] = reqContent
|
||||
map["timestamp"] = sdf.format(Date())
|
||||
map["serialNo"] = ptDate(Global.ptPlatformAlias)
|
||||
map["serialNo"] = ptDate()
|
||||
map["sign"] = RSAUtil.sign(RSAUtil.getSignatureContent(map), Global.ptPrivateKey) ?: ""
|
||||
return Json.encodeToString(map)
|
||||
}
|
||||
@@ -157,26 +154,34 @@ object PTClient {
|
||||
val encryptedContent = mutableMap["content"]
|
||||
?: throw IllegalStateException("content 为空")
|
||||
|
||||
val plainContent = SecurityUtil.decrypt3DES(Global.ptPassword, encryptedContent) ?: ""
|
||||
val plainContent =
|
||||
SecurityUtil.decrypt3DES(Global.ptPassword, encryptedContent)
|
||||
?.trim()
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?: "{}"
|
||||
|
||||
val contentElement = runCatching {
|
||||
Json.parseToJsonElement(plainContent)
|
||||
}.getOrElse {
|
||||
JsonObject(emptyMap())
|
||||
}
|
||||
|
||||
// 5. 替换 content
|
||||
val resultJson = buildJsonObject {
|
||||
json.forEach { (k, v) ->
|
||||
if (k == "content") {
|
||||
put("content", Json.parseToJsonElement(plainContent))
|
||||
put("content", contentElement)
|
||||
} else {
|
||||
put(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultJson.toString()
|
||||
}
|
||||
|
||||
fun ptDate(prefix: String?): String {
|
||||
fun ptDate(): String {
|
||||
val date = Date()
|
||||
val sdf = SimpleDateFormat("YYYYMMddHHmmss")
|
||||
val str = prefix + sdf.format(date) + (Math.random() * 90 + 10).toInt()
|
||||
val str = Global.ptPlatformAlias + sdf.format(date) + (Math.random() * 90 + 10).toInt()
|
||||
println(str)
|
||||
return str
|
||||
}
|
||||
|
||||
+3
-1
@@ -30,7 +30,9 @@ http.interceptors.response.use(
|
||||
return response.data
|
||||
}
|
||||
if (payload.code !== '0') {
|
||||
throw new BizError(payload.code, payload.message || '请求失败', payload.traceId ?? traceId)
|
||||
const errMsg = payload.message || '请求失败'
|
||||
message.error(payload.traceId ?? traceId ? `${errMsg}(追踪ID:${payload.traceId ?? traceId})` : errMsg)
|
||||
throw new BizError(payload.code, errMsg, payload.traceId ?? traceId)
|
||||
}
|
||||
return payload.data
|
||||
},
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
import http from '@/api/http'
|
||||
|
||||
/**
|
||||
* 账号状态
|
||||
* 0:无需认证
|
||||
* 1:风险认证
|
||||
* 2:登录认证
|
||||
* 3:风险+登录认证
|
||||
*/
|
||||
export type AuthStatus = '0' | '1' | '2' | '3'
|
||||
|
||||
/**
|
||||
* 登录身份类型
|
||||
* 01:法定代表人
|
||||
* 02:财务负责人
|
||||
* 03:办税员
|
||||
* 04:涉税服务人员
|
||||
* 05:管理员
|
||||
* 07:领票人
|
||||
* 09:开票员
|
||||
* 99:其他人员
|
||||
*/
|
||||
export type IdentityType = '01' | '02' | '03' | '04' | '05' | '07' | '09' | '99'
|
||||
|
||||
/**
|
||||
* 操作建议
|
||||
* 0:无需认证
|
||||
* 1:需扫码认证
|
||||
* 2:需扫码或短信认证
|
||||
* 3:需短信认证
|
||||
*/
|
||||
export type OperationProposed = '0' | '1' | '2' | '3'
|
||||
|
||||
/**
|
||||
* 可切换状态
|
||||
* 0:不可切换
|
||||
* 1:可切换,代表该数电账号在该地区的其他企业有登录状态
|
||||
*/
|
||||
export type Switchable = '0' | '1'
|
||||
|
||||
/**
|
||||
* 微信绑定状态
|
||||
* 0:否
|
||||
* 1:是
|
||||
*/
|
||||
export type WechatBindStatus = '0' | '1'
|
||||
|
||||
/**
|
||||
* 登录认证状态
|
||||
* 0:未登录
|
||||
* 1:已登录
|
||||
*/
|
||||
export type LoginAuthStatus = '0' | '1'
|
||||
|
||||
/**
|
||||
* 风险认证状态
|
||||
* 0:未认证
|
||||
* 1:已认证
|
||||
*/
|
||||
export type RiskAuthStatus = '0' | '1'
|
||||
|
||||
export interface TaxBureauAccountAuth {
|
||||
/** 姓名 */
|
||||
name: string
|
||||
/** 销售方纳税人识别号 (15-20位) */
|
||||
taxpayerNum: string
|
||||
/** 电子税局登录账号 */
|
||||
account: string
|
||||
/** 登录身份类型: 01法定代表人, 02财务负责人, 03办税员, 04涉税服务人员, 05管理员, 07领票人, 09开票员, 99其他人员 */
|
||||
identityType: string
|
||||
/** 操作建议: 0无需认证, 1需扫码认证, 2需扫码或短信认证, 3需短信认证 */
|
||||
operationProposed: string
|
||||
/** 账号状态: 0无需认证, 1风险认证, 2登录认证, 3风险+登录认证 */
|
||||
authStatus: string
|
||||
/** 当前企业是否可切换: 0不可切换, 1可切换 */
|
||||
switchable: string
|
||||
/** 是否绑定微信公众号: 0否, 1是 */
|
||||
wechatUserBindStatus: string
|
||||
/** 最新认证成功时间 (yyyy-MM-dd HH:mm:ss) */
|
||||
lastAuthSuccTime: string
|
||||
/** 登录认证状态: 0未登录, 1已登录 */
|
||||
loginAuthStatus: string
|
||||
/** 最新登录认证时间 (yyyy-MM-dd HH:mm:ss) */
|
||||
lastLoginAuthTime: string
|
||||
/** 风险认证状态: 0未认证, 1已认证 */
|
||||
riskAuthStatus: string
|
||||
/** 最新风险认证时间 (yyyy-MM-dd HH:mm:ss) */
|
||||
lastRiskAuthTime: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取票通账号认证状态信息
|
||||
*/
|
||||
export function getPTInfoApi(): Promise<TaxBureauAccountAuth> {
|
||||
return http.get('/pt/info')
|
||||
}
|
||||
|
||||
export interface TaxEnterpriseRegisterRequest {
|
||||
taxpayerNum: string
|
||||
enterpriseName: string
|
||||
legalPersonName: string
|
||||
contactsName: string
|
||||
contactsEmail: string
|
||||
contactsPhone: string
|
||||
regionCode: string
|
||||
cityName: string
|
||||
enterpriseAddress: string
|
||||
taxRegistrationCertificate: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册企业(纳税人)
|
||||
*/
|
||||
export function registerEnterpriseApi(payload: TaxEnterpriseRegisterRequest): Promise<string> {
|
||||
return http.post('/pt/register', payload)
|
||||
}
|
||||
|
||||
export interface TaxRegisterUserRequest {
|
||||
taxpayerNum: string
|
||||
taxAccount: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记账号
|
||||
*/
|
||||
export function registerUserApi(payload: TaxRegisterUserRequest): Promise<string> {
|
||||
return http.post('/pt/registerUser', payload)
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 基础信息配置(本地 CRUD)
|
||||
// =============================================
|
||||
|
||||
/** 企业信息 */
|
||||
export interface EnterpriseInfo {
|
||||
taxpayerNum: string
|
||||
enterpriseName: string
|
||||
legalPersonName: string
|
||||
contactsName: string
|
||||
contactsEmail: string
|
||||
contactsPhone: string
|
||||
regionCode: string
|
||||
cityName: string
|
||||
enterpriseAddress: string
|
||||
taxRegistrationCertificate: string
|
||||
}
|
||||
|
||||
/** 获取企业信息 */
|
||||
export function getEnterpriseInfoApi(): Promise<EnterpriseInfo> {
|
||||
return http.get('/pt/enterprise')
|
||||
}
|
||||
|
||||
/** 更新企业信息 */
|
||||
export function updateEnterpriseInfoApi(payload: Partial<EnterpriseInfo>): Promise<string> {
|
||||
return http.put('/pt/enterprise', payload)
|
||||
}
|
||||
|
||||
/** 数电账号信息 */
|
||||
export interface DigitalAccountInfo {
|
||||
taxpayerNum: string
|
||||
taxAccount: string
|
||||
}
|
||||
|
||||
/** 获取数电账号信息 */
|
||||
export function getDigitalAccountApi(): Promise<DigitalAccountInfo> {
|
||||
return http.get('/pt/digital-account')
|
||||
}
|
||||
|
||||
/** 更新数电账号信息 */
|
||||
export function updateDigitalAccountApi(payload: DigitalAccountInfo): Promise<string> {
|
||||
return http.put('/pt/digital-account', payload)
|
||||
}
|
||||
|
||||
/** 开票预设数据 */
|
||||
export interface PresetData {
|
||||
bankName: string
|
||||
bankAccount: string
|
||||
}
|
||||
|
||||
/** 获取开票预设数据 */
|
||||
export function getPresetDataApi(): Promise<PresetData> {
|
||||
return http.get('/pt/preset')
|
||||
}
|
||||
|
||||
/** 更新开票预设数据 */
|
||||
export function updatePresetDataApi(payload: PresetData): Promise<string> {
|
||||
return http.put('/pt/preset', payload)
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// 开票相关
|
||||
// =============================================
|
||||
|
||||
/** 发票商品项目 */
|
||||
export interface InvoiceItem {
|
||||
/** 商品/服务名称 */
|
||||
goodsName: string
|
||||
/** 税收分类编码 */
|
||||
taxClassificationCode: string
|
||||
/** 规格型号 */
|
||||
specificationModel?: string
|
||||
/** 计量单位 */
|
||||
meteringUnit?: string
|
||||
/** 数量 */
|
||||
quantity?: string
|
||||
/** 含税标识:0不含税,1含税 */
|
||||
includeTaxFlag?: string
|
||||
/** 单价 */
|
||||
unitPrice?: string
|
||||
/** 金额 */
|
||||
invoiceAmount: string
|
||||
/** 税率 如 0.13 = 13% */
|
||||
taxRateValue: string
|
||||
/** 税额 */
|
||||
taxRateAmount?: string
|
||||
/** 折扣金额(负数) */
|
||||
discountAmount?: string
|
||||
/** 折扣税额 */
|
||||
discountTaxRateAmount?: string
|
||||
/** 优惠政策标识:1使用优惠政策 */
|
||||
preferentialPolicyFlag?: string
|
||||
/** 零税率标识:1免税,2不征税,3普通零税率 */
|
||||
zeroTaxFlag?: string
|
||||
/** 增值税特殊管理说明 */
|
||||
vatSpecialManage?: string
|
||||
}
|
||||
|
||||
/** 差额征税凭证明细 */
|
||||
export interface VariableLevyProof {
|
||||
/** 凭证类型:01数电票,02增值税专票,03增值税普票 */
|
||||
proofType: string
|
||||
/** 数电票号码 */
|
||||
electronicInvoiceNo?: string
|
||||
/** 发票代码 */
|
||||
invoiceCode?: string
|
||||
/** 发票号码 */
|
||||
invoiceNo?: string
|
||||
/** 凭证号码 */
|
||||
proofNo?: string
|
||||
/** 开具日期 yyyy-MM-dd */
|
||||
issueDate?: string
|
||||
/** 凭证总金额 */
|
||||
proofAmount: string
|
||||
/** 本次扣除金额 */
|
||||
deductionAmount: string
|
||||
/** 备注 */
|
||||
proofRemark?: string
|
||||
/** 来源 */
|
||||
source?: string
|
||||
}
|
||||
|
||||
/** 订单信息 */
|
||||
export interface OrderInfo {
|
||||
/** 业务单据号 */
|
||||
orderNo: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 数电发票开票请求
|
||||
*/
|
||||
export interface InvoiceRequest {
|
||||
/** 销方纳税人识别号 */
|
||||
taxpayerNum: string
|
||||
/** 发票请求流水号 */
|
||||
invoiceReqSerialNo: string
|
||||
/** 开票种类:81数电专票,82数电普票,10电子普票,08电子专票 */
|
||||
invoiceIssueKindCode?: string
|
||||
/** 购买方名称(发票抬头) */
|
||||
buyerName: string
|
||||
/** 农产品收购发票销售方证件类型:201居民身份证, 208外国护照, 210港澳居民来往内地通行证, 213台湾居民来往大陆通行证, 215外国人居留证, 219香港永久性居民身份证, 220台湾身份证, 221澳门特别行政区永久性居民身份证, 233外国人永久居留身份证, 103税务登记证, 299其他个人证件 */
|
||||
purchaseInvSellerIdType?: string
|
||||
/** 购买方纳税人识别号 */
|
||||
buyerTaxpayerNum?: string
|
||||
/** 是否自然人:0否,1是 */
|
||||
naturalPersonFlag?: string
|
||||
/** 购买方地址 */
|
||||
buyerAddress?: string
|
||||
/** 购买方电话 */
|
||||
buyerTel?: string
|
||||
/** 购买方开户银行 */
|
||||
buyerBankName?: string
|
||||
/** 购买方银行账号 */
|
||||
buyerBankAccount?: string
|
||||
/** 销方地址 */
|
||||
sellerAddress?: string
|
||||
/** 销方电话 */
|
||||
sellerTel?: string
|
||||
/** 销方开户银行 */
|
||||
sellerBankName?: string
|
||||
/** 销方银行账号 */
|
||||
sellerBankAccount?: string
|
||||
/** 备注显示购买方银行:0不显示,1显示 */
|
||||
showBuyerBank?: string
|
||||
/** 备注显示销售方银行:0不显示,1显示 */
|
||||
showSellerBank?: string
|
||||
/** 备注显示购买方地址电话 */
|
||||
showBuyerAddrTel?: string
|
||||
/** 备注显示销售方地址电话 */
|
||||
showSellerAddrTel?: string
|
||||
/** 开票员税局账号 */
|
||||
account?: string
|
||||
/** 差额征税标识:1全额,2差额 */
|
||||
variableLevyFlag?: string
|
||||
/** 收款人名称 */
|
||||
casherName?: string
|
||||
/** 复核人名称 */
|
||||
reviewerName?: string
|
||||
/** 收票人姓名 */
|
||||
takerName?: string
|
||||
/** 收票人手机号 */
|
||||
takerTel?: string
|
||||
/** 收票人邮箱 */
|
||||
takerEmail?: string
|
||||
/** 特殊票种:08成品油,02农产品收购,12自产农产品 */
|
||||
specialInvoiceKind?: string
|
||||
/** 发票备注 */
|
||||
remark?: string
|
||||
/** 自定义数据 */
|
||||
definedData?: string
|
||||
/** 业务订单号 */
|
||||
tradeNo?: string
|
||||
/** 门店编号(集团版) */
|
||||
shopNum?: string
|
||||
/** 发票商品明细 */
|
||||
itemList: InvoiceItem[]
|
||||
/** 差额征税凭证明细 */
|
||||
variableLevyProofList?: VariableLevyProof[]
|
||||
/** 订单列表(支持合并开票) */
|
||||
orderList?: OrderInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交开票
|
||||
*/
|
||||
export function invoiceIssueApi(payload: InvoiceRequest): Promise<string> {
|
||||
return http.post('/pt/invoiceBlue', payload)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="placeholder">
|
||||
<h2>开票历史</h2>
|
||||
<p>功能开发中,敬请期待</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
min-height: 100%;
|
||||
background: #f7f8fa;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.placeholder h2 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.placeholder p {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,14 @@ export interface CurrentUserProfile {
|
||||
realName?: string | null
|
||||
orgId?: string | null
|
||||
status: string
|
||||
avatar?: string | null
|
||||
email?: string | null
|
||||
phone?: string | null
|
||||
createdAt?: string | null
|
||||
taxpayerNum?: string | null
|
||||
account?: string | null
|
||||
taxPassword?: string | null
|
||||
taxIdentityType?: string | null
|
||||
}
|
||||
|
||||
export interface MenuNode {
|
||||
|
||||
@@ -23,6 +23,10 @@ export interface UserDetail {
|
||||
status: string
|
||||
statusLabel?: string
|
||||
roleIds: string[]
|
||||
taxpayerNum?: string | null
|
||||
account?: string | null
|
||||
taxPassword?: string | null
|
||||
taxIdentityType?: string | null
|
||||
}
|
||||
|
||||
export interface UserQuery {
|
||||
@@ -55,6 +59,10 @@ export interface UpdateUserRequest {
|
||||
email?: string
|
||||
avatar?: string
|
||||
orgId?: string
|
||||
taxpayerNum?: string
|
||||
account?: string
|
||||
taxPassword?: string
|
||||
taxIdentityType?: string
|
||||
}
|
||||
|
||||
export interface UpdateUserStatusRequest {
|
||||
|
||||
Reference in New Issue
Block a user