From 7046ab9850eeeb694d4bf0acce47cc74bd960906 Mon Sep 17 00:00:00 2001 From: jingrow Date: Fri, 1 Aug 2025 02:12:18 +0800 Subject: [PATCH] =?UTF-8?q?dashboard=E5=B7=A6=E4=BE=A7=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=9F=9F=E5=90=8D=E8=8F=9C=E5=8D=95=E5=8F=8A=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src2/components/JsiteDomainOverview.vue | 417 ++++++++ .../components/JsiteDomainRenewalDialog.vue | 222 ++++ .../components/JsiteDomainTransferDialog.vue | 204 ++++ dashboard/src2/components/NavigationItems.vue | 9 + dashboard/src2/objects/domain.js | 258 +++++ dashboard/src2/objects/index.js | 2 + dashboard/src2/pages/NewJsiteDomain.vue | 952 ++++++++++++++++++ dashboard/src2/router.js | 21 + jcloud/api/billing.py | 83 ++ jcloud/api/client.py | 1 + jcloud/api/domain_west.py | 376 ++++++- .../pagetype/jsite_domain/jsite_domain.py | 36 +- jcloud/jcloud/pagetype/order/order.json | 6 +- jcloud/jcloud/pagetype/order/order.py | 2 +- 14 files changed, 2545 insertions(+), 44 deletions(-) create mode 100644 dashboard/src2/components/JsiteDomainOverview.vue create mode 100644 dashboard/src2/components/JsiteDomainRenewalDialog.vue create mode 100644 dashboard/src2/components/JsiteDomainTransferDialog.vue create mode 100644 dashboard/src2/objects/domain.js create mode 100644 dashboard/src2/pages/NewJsiteDomain.vue diff --git a/dashboard/src2/components/JsiteDomainOverview.vue b/dashboard/src2/components/JsiteDomainOverview.vue new file mode 100644 index 0000000..1464a66 --- /dev/null +++ b/dashboard/src2/components/JsiteDomainOverview.vue @@ -0,0 +1,417 @@ + + + \ No newline at end of file diff --git a/dashboard/src2/components/JsiteDomainRenewalDialog.vue b/dashboard/src2/components/JsiteDomainRenewalDialog.vue new file mode 100644 index 0000000..9d10ea1 --- /dev/null +++ b/dashboard/src2/components/JsiteDomainRenewalDialog.vue @@ -0,0 +1,222 @@ + + + \ No newline at end of file diff --git a/dashboard/src2/components/JsiteDomainTransferDialog.vue b/dashboard/src2/components/JsiteDomainTransferDialog.vue new file mode 100644 index 0000000..6a519ce --- /dev/null +++ b/dashboard/src2/components/JsiteDomainTransferDialog.vue @@ -0,0 +1,204 @@ + + + \ No newline at end of file diff --git a/dashboard/src2/components/NavigationItems.vue b/dashboard/src2/components/NavigationItems.vue index e5bfbb1..5fe6b57 100644 --- a/dashboard/src2/components/NavigationItems.vue +++ b/dashboard/src2/components/NavigationItems.vue @@ -109,6 +109,15 @@ export default { condition: onboardingComplete && !isSaasUser && this.$team.pg.is_pro, disabled: enforce2FA, }, + { + name: '域名', + icon: () => h(Globe), + route: '/domains', + isActive: + ['Jsite Domain List', 'New Jsite Domain'].includes(routeName) || + routeName.startsWith('Jsite Domain'), + disabled: enforce2FA, + }, { name: '服务器', icon: () => h(Server), diff --git a/dashboard/src2/objects/domain.js b/dashboard/src2/objects/domain.js new file mode 100644 index 0000000..d24a08c --- /dev/null +++ b/dashboard/src2/objects/domain.js @@ -0,0 +1,258 @@ +import { defineAsyncComponent, h } from 'vue'; +import LucideGlobe from '~icons/lucide/globe'; +import { getTeam } from '../data/team'; +import router from '../router'; +import { icon } from '../utils/components'; +import { duration, planTitle, userCurrency } from '../utils/format'; +import { trialDays } from '../utils/site'; +import { getJobsTab } from './common/jobs'; +import { tagTab } from './common/tags'; + +export default { + pagetype: 'Jsite Domain', + whitelistedMethods: { + renew: 'renew', + rename: 'rename', + dropDomain: 'drop_domain', + addTag: 'add_resource_tag', + removeTag: 'remove_resource_tag' + }, + list: { + route: '/domains', + title: '域名', + fields: [ + 'name', + 'domain', + 'status', + 'domain_owner', + 'domain_registrar', + 'registration_date', + 'end_date', + 'price', + 'period', + 'auto_renew', + 'team', + 'order_id', + 'description' + ], + filterControls() { + return [ + { + type: 'select', + label: '状态', + fieldname: 'status', + options: [ + { label: '', value: '' }, + { label: '待处理', value: 'Pending' }, + { label: '活跃', value: 'Active' }, + { label: '已过期', value: 'Expired' }, + { label: '已暂停', value: 'Suspended' }, + { label: '已取消', value: 'Cancelled' } + ] + }, + { + type: 'select', + label: '自动续费', + fieldname: 'auto_renew', + options: [ + { label: '', value: '' }, + { label: '是', value: '1' }, + { label: '否', value: '0' } + ] + } + ]; + }, + orderBy: 'creation desc', + searchField: 'domain', + columns: [ + { + label: '域名', + fieldname: 'domain', + width: 2, + class: 'font-medium', + format(value) { + return value; + } + }, + { + label: '状态', + fieldname: 'status', + type: 'Badge', + width: 0.8, + format(value) { + const statusMap = { + 'Pending': '待处理', + 'Active': '活跃', + 'Expired': '已过期', + 'Suspended': '已暂停', + 'Cancelled': '已取消' + }; + return statusMap[value] || value; + } + }, + { + label: '所有者', + fieldname: 'domain_owner', + format(value) { + return value || '-'; + } + }, + { + label: '注册商', + fieldname: 'domain_registrar', + format(value) { + return value || '-'; + } + }, + { + label: '注册时间', + fieldname: 'registration_date', + format(value) { + if (!value) return '-'; + return value; + } + }, + { + label: '到期时间', + fieldname: 'end_date', + format(value) { + if (!value) return '-'; + return value; + } + }, + { + label: '价格', + fieldname: 'price', + format(value) { + if (!value) return '-'; + return `¥${value}/年`; + } + } + ], + primaryAction({ listResource: domains }) { + return { + label: '新建域名', + variant: 'solid', + slots: { + prefix: icon('plus') + }, + onClick() { + router.push('/domains/new'); + } + }; + }, + statusBadge({ documentResource: domain }) { + const status = domain.pg?.status; + const statusMap = { + 'Pending': '待处理', + 'Active': '活跃', + 'Expired': '已过期', + 'Suspended': '已暂停', + 'Cancelled': '已取消' + }; + return { + label: statusMap[status] || status + }; + }, + breadcrumbs({ documentResource: domain }) { + return [ + { + label: '域名', + route: '/domains' + }, + { + label: domain.pg?.domain || domain.pg?.name, + route: `/domains/${domain.pg?.name}` + } + ]; + }, + actions({ documentResource: domain }) { + if (!domain) return []; + + const actions = [ + { + label: '续费', + icon: 'refresh-cw', + onClick() { + domain.renew.submit(); + }, + condition: () => domain.pg?.status === 'Active' + }, + { + label: '重命名', + icon: 'edit-3', + onClick() { + domain.rename.submit(); + } + }, + { + label: '删除', + icon: 'trash-2', + onClick() { + domain.dropDomain.submit(); + }, + condition: () => domain.pg?.status !== 'Active' + } + ]; + return actions.filter(action => !action.condition || action.condition()); + } + }, + detail: { + route: '/domains/:name', + title: '域名详细信息', + tabs: [ + { + label: '概览', + route: '', + type: 'Component', + component: defineAsyncComponent(() => import('../components/JsiteDomainOverview.vue')), + props: domain => { + return { domain: domain.pg?.name }; + } + } + ], + fields: [ + { + label: '基本信息', + fields: [ + 'domain', + 'status', + 'domain_owner', + 'domain_registrar', + 'registration_date', + 'end_date' + ] + }, + { + label: '价格信息', + fields: [ + 'price', + 'period', + 'auto_renew', + 'order_id' + ] + }, + { + label: 'DNS设置', + fields: [ + 'dns_host1', + 'dns_host2', + 'dns_host3', + 'dns_host4', + 'dns_host5', + 'dns_host6' + ] + }, + { + label: '其他信息', + fields: [ + 'description', + 'whois_protection' + ] + } + ], + actions({ documentResource: domain }) { + return []; + } + } +}; \ No newline at end of file diff --git a/dashboard/src2/objects/index.js b/dashboard/src2/objects/index.js index 55072f3..589ec91 100644 --- a/dashboard/src2/objects/index.js +++ b/dashboard/src2/objects/index.js @@ -5,6 +5,7 @@ import marketplace from './marketplace'; import server from './server'; import jsite_server from './jsite_server'; import notification from './notification'; +import domain from './domain'; let objects = { Site: site, @@ -13,6 +14,7 @@ let objects = { Marketplace: marketplace, Server: server, 'Jsite Server': jsite_server, + 'Jsite Domain': domain, Notification: notification }; diff --git a/dashboard/src2/pages/NewJsiteDomain.vue b/dashboard/src2/pages/NewJsiteDomain.vue new file mode 100644 index 0000000..15d72ef --- /dev/null +++ b/dashboard/src2/pages/NewJsiteDomain.vue @@ -0,0 +1,952 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src2/router.js b/dashboard/src2/router.js index 1fe6c75..03fce2d 100644 --- a/dashboard/src2/router.js +++ b/dashboard/src2/router.js @@ -131,6 +131,27 @@ let router = createRouter({ return { objectType: 'Jsite Server', ...route.params }; } }, + { + name: 'Jsite Domain List', + path: '/domains', + component: () => import('./pages/ListPage.vue'), + props: route => { + return { objectType: 'Jsite Domain', ...route.params }; + } + }, + { + name: 'New Jsite Domain', + path: '/domains/new', + component: () => import('./pages/NewJsiteDomain.vue'), + }, + { + name: 'Jsite Domain Detail', + path: '/domains/:name', + component: () => import('./pages/DetailPage.vue'), + props: route => { + return { objectType: 'Jsite Domain', ...route.params }; + } + }, { name: 'Billing', path: '/billing', diff --git a/jcloud/api/billing.py b/jcloud/api/billing.py index 82407df..5fb1294 100644 --- a/jcloud/api/billing.py +++ b/jcloud/api/billing.py @@ -1098,6 +1098,12 @@ def handle_order_payment_complete(order_id): elif order.order_type == "新建服务器": # 异步创建服务器 jingrow.enqueue('jcloud.api.aliyun_server_light.create_aliyun_server', order_name=order.name) + elif order.order_type == "域名注册": + # 异步注册域名 + jingrow.enqueue('jcloud.api.domain_west.register_domain_from_order', order_name=order.name) + elif order.order_type == "域名续费": + # 异步续费域名 + jingrow.enqueue('jcloud.api.domain_west.renew_domain_from_order', order_name=order.name) return True except Exception as e: @@ -1948,3 +1954,80 @@ def get_balance_transactions(page=1, page_size=20, search=None): "error": str(e) } +@jingrow.whitelist() +def process_balance_payment_for_domain_order(order_id): + """处理域名订单的余额支付""" + try: + # 获取当前用户团队 + team = get_current_team(True) + + # 获取订单信息 + order = jingrow.get_pg("Order", {"order_id": order_id}) + if not order: + jingrow.throw(f"找不到订单: {order_id}") + + # 验证订单是否属于当前团队 + if order.team != team.name: + jingrow.throw("您没有权限支付此订单") + + # 检查订单状态 + if order.status != "待支付": + return { + "success": False, + "message": "该订单已支付或已取消" + } + + # 使用 Team 类的 get_balance 方法获取余额 + balance = team.get_balance() + + # 检查余额是否足够 + if balance < order.total_amount: + return { + "success": False, + "message": "余额不足" + } + + # 创建余额交易记录(扣款) + balance_transaction = jingrow.get_pg({ + "pagetype": "Balance Transaction", + "team": team.name, + "type": "Adjustment", + "source": "Prepaid Credits", + "amount": -1 * float(order.total_amount), # 使用负数表示扣减 + "description": f"{order.order_type}-{order.title}", + "paid_via_local_pg": 1 + }) + balance_transaction.flags.ignore_permissions = True + balance_transaction.insert() + balance_transaction.submit() + + # 更新订单状态 + order.status = "已支付" + order.payment_method = "余额支付" + order.save(ignore_permissions=True) + jingrow.db.commit() + + # 支付成功,订单状态已更新 + + # 调用统一的订单支付完成处理函数 + handle_order_payment_complete(order_id) + + return { + "status": "Success", + "message": "支付成功", + "order": order.as_dict() + } + + except Exception as e: + jingrow.log_error("支付错误", f"域名订单余额支付失败: {str(e)}") + return { + "status": "Error", + "message": f"余额支付失败: {str(e)}" + } + + + + + + + diff --git a/jcloud/api/client.py b/jcloud/api/client.py index 0202561..1d4e423 100644 --- a/jcloud/api/client.py +++ b/jcloud/api/client.py @@ -78,6 +78,7 @@ ALLOWED_PAGETYPES = [ "Jcloud Settings", "Mpesa Payment Record", "Jsite Server", + "Jsite Domain", ] ALLOWED_PAGETYPES_FOR_SUPPORT = [ diff --git a/jcloud/api/domain_west.py b/jcloud/api/domain_west.py index 872bf4e..e0956e9 100644 --- a/jcloud/api/domain_west.py +++ b/jcloud/api/domain_west.py @@ -408,7 +408,33 @@ def check_domain(domain: str, suffix: str = '.com'): if not domain: return {"status": "error", "message": "缺少域名参数"} - return client.query_domain(domain, suffix) + response = client.query_domain(domain, suffix) + + # 添加调试日志 + jingrow.log_error("域名查询调试", f"domain={domain}, suffix={suffix}, response={response}") + + if response.get("status") == "error": + return response + + try: + # 直接检查响应格式 + if response.get("result") != 200: + return {"status": "error", "message": "API查询失败"} + + full_domain = domain + suffix + for item in response.get("data", []): + if item.get("name") == full_domain: + return { + "available": item.get("avail", 0) == 1, + "domain": full_domain, + "message": "域名可用" if item.get("avail", 0) == 1 else "域名已被注册" + } + + return {"status": "error", "message": f"未找到域名 {full_domain} 的查询结果"} + + except Exception as e: + jingrow.log_error("域名查询响应解析失败", error=str(e)) + return {"status": "error", "message": "域名查询响应解析失败"} @jingrow.whitelist() @@ -421,7 +447,28 @@ def get_west_domain_price(domain: str, year: int = 1): if not domain: return {"status": "error", "message": "缺少域名参数"} - return client.get_domain_price(domain, year) + response = client.get_domain_price(domain, year) + + if response.get("status") == "error": + return response + + try: + # 直接检查响应格式 + if response.get("result") != 200: + return {"status": "error", "message": "API查询失败"} + + data = response.get("data", {}) + return { + "data": { + "price": data.get("buyprice", 0), + "domain": domain, + "year": year + } + } + + except Exception as e: + jingrow.log_error("域名价格查询响应解析失败", error=str(e)) + return {"status": "error", "message": "域名价格查询响应解析失败"} @jingrow.whitelist() @@ -628,41 +675,294 @@ def west_domain_get_template_detail(**data): return client.get_template_detail(template_id) -# 便捷函数 -def call_west_domain_api(api_name: str, **kwargs) -> Dict[str, Any]: - """ - 调用西部数码域名API的通用函数 - - Args: - api_name: API名称 - **kwargs: API参数 - - Returns: - API响应结果 - """ - api_functions = { - 'check_balance': check_west_balance, - 'query': check_domain, - 'get_price': get_west_domain_price, - 'register': west_domain_register, - 'renew': west_domain_renew, - 'get_list': west_domain_get_list, - 'get_info': west_domain_get_info, - 'get_dns': west_domain_get_dns, - 'modify_dns': west_domain_modify_dns, - 'add_dns_record': west_domain_add_dns_record, - 'delete_dns_record': west_domain_delete_dns_record, - 'transfer': west_domain_transfer, - 'lock': west_domain_lock, - 'get_templates': west_domain_get_templates, - 'get_template_detail': west_domain_get_template_detail, - } - - if api_name not in api_functions: - return {"status": "error", "message": f"未知的API: {api_name}"} - +@jingrow.whitelist() +def create_domain_order(domain, period=1, payment_method='balance'): + """创建域名注册订单""" try: - return api_functions[api_name](**kwargs) + # 获取当前用户团队 + team = get_current_team(True) + + # 验证域名格式 + if not domain or '.' not in domain: + return {"success": False, "message": "域名格式不正确"} + + # 查询域名价格 + client = get_west_client() + if not client: + return {"success": False, "message": "API客户端初始化失败"} + + price_result = client.get_domain_price(domain, 1) + if price_result.get("status") == "error": + return {"success": False, "message": "获取域名价格失败"} + + # 计算总价格 + yearly_price = price_result.get("data", {}).get("price", 50) # 默认50元/年 + total_amount = yearly_price * period + + # 生成订单号 + order_id = f"DOMAIN_{jingrow.utils.random_string(6)}" + + # 创建订单记录 + order = jingrow.get_pg({ + "pagetype": "Order", + "order_id": order_id, + "order_type": "域名注册", + "team": team.name, + "status": "待支付", + "total_amount": total_amount, + "title": domain, + "description": f"{period}年" + }) + order.insert(ignore_permissions=True) + + # 创建域名记录 + domain_doc = jingrow.get_pg({ + "pagetype": "Jsite Domain", + "domain": domain, + "team": team.name, + "order_id": order_id, + "status": "Pending", + "price": yearly_price, + "period": period, + "domain_registrar": "西部数码", + "auto_renew": False, + "whois_protection": True + }) + domain_doc.insert(ignore_permissions=True) + + jingrow.db.commit() + + return { + "success": True, + "message": "订单创建成功", + "order": order.as_dict(), + "domain": domain_doc.as_dict() + } + except Exception as e: - jingrow.log_error(f"西部数码域名API调用失败: {api_name}", error=str(e), params=kwargs) - return {"status": "error", "message": f"API调用失败: {str(e)}"} + jingrow.log_error("域名订单", f"创建域名订单失败: {str(e)}") + return {"success": False, "message": f"创建订单失败: {str(e)}"} + + +@jingrow.whitelist() +def create_domain_renew_order(**kwargs): + """创建域名续费订单""" + try: + domain = kwargs.get('domain') + renewal_years = kwargs.get('renewal_years', 1) + + if not domain: + jingrow.throw("缺少域名信息") + + # 验证输入 + domain_pg = jingrow.get_pg("Jsite Domain", domain) + if not domain_pg: + jingrow.throw("域名不存在") + + team = domain_pg.team + + # 验证当前用户权限 + current_team = get_current_team(True) + if current_team.name != team: + jingrow.throw("您没有权限为此域名创建续费订单") + + # 计算续费金额 + renewal_years = int(renewal_years) + yearly_price = domain_pg.price or 0 + total_amount = yearly_price * renewal_years + + # 生成唯一订单号 + order_id = f"DOMAIN_RENEW_{jingrow.utils.random_string(6)}" + + # 创建订单记录 + order = jingrow.get_pg({ + "pagetype": "Order", + "order_id": order_id, + "order_type": "域名续费", + "team": team, + "status": "待支付", + "total_amount": total_amount, + "title": domain_pg.domain, + "description": str(renewal_years) # 存储续费年数 + }) + + order.insert(ignore_permissions=True) + jingrow.db.commit() + + return { + "success": True, + "order": order.as_dict() + } + except Exception as e: + jingrow.log_error("域名续费订单错误", f"创建域名续费订单失败: {str(e)}") + return { + "success": False, + "message": f"创建续费订单失败: {str(e)}" + } + + +def register_domain_from_order(order_name): + """支付成功后异步注册域名""" + try: + order = jingrow.get_pg("Order", order_name) + if not order: + raise Exception("订单不存在") + + # 查找对应的域名记录(通过订单ID) + domain = jingrow.get_pg("Jsite Domain", {"order_id": order.order_id}) + if not domain: + raise Exception("找不到对应的域名记录") + + # 从域名记录中获取配置信息 + domain_name = domain.domain + period = domain.period or 1 + + # 调用西部数码API注册域名 + result = call_west_domain_api("register", domain=domain_name, regyear=period) + + # 打印result到后台日志 + jingrow.log_error("西部数码域名注册结果", f"订单 {order_name} 的注册结果: {result}") + + if not result or not result.get('success'): + raise Exception(f"域名注册失败: {result.get('message', '未知错误')}") + + # 更新域名记录状态 + domain.status = "Active" + domain.registration_date = jingrow.utils.nowdate() + domain.end_date = jingrow.utils.add_months(jingrow.utils.nowdate(), period * 12) + domain.save(ignore_permissions=True) + + # 更新订单状态 + order.status = "交易成功" + order.save(ignore_permissions=True) + + jingrow.db.commit() + + return True + + except Exception as e: + jingrow.log_error("域名注册失败", f"订单 {order_name}: {str(e)}") + raise e + + +def renew_domain_from_order(order_name): + """支付成功后异步续费域名""" + try: + order = jingrow.get_pg("Order", order_name) + if not order: + raise Exception("订单不存在") + + # 从订单中获取信息 + domain_name = order.title # 域名 + renewal_years = int(order.description) # 续费年数 + + # 查找域名记录 + domain = jingrow.get_pg("Jsite Domain", {"domain": domain_name}) + if not domain: + raise Exception("找不到对应的域名记录") + + # 调用西部数码API续费域名 + result = call_west_domain_api("renew", domain=domain_name, regyear=renewal_years) + + # 打印result到后台日志 + jingrow.log_error("西部数码域名续费结果", f"订单 {order_name} 的续费结果: {result}") + + if not result or not result.get('success'): + raise Exception(f"域名续费失败: {result.get('message', '未知错误')}") + + # 更新域名到期时间 + domain.end_date = jingrow.utils.add_months(domain.end_date or jingrow.utils.nowdate(), renewal_years * 12) + domain.save(ignore_permissions=True) + + # 更新订单状态 + order.status = "交易成功" + order.save(ignore_permissions=True) + + jingrow.db.commit() + + return True + + except Exception as e: + jingrow.log_error("域名续费失败", f"订单 {order_name}: {str(e)}") + raise e + + +@jingrow.whitelist() +def toggle_domain_auto_renew(pagetype, name, auto_renew): + """切换域名自动续费状态""" + try: + # 获取当前用户团队 + team = get_current_team(True) + + # 获取域名记录 + domain = jingrow.get_pg(pagetype, name) + if not domain: + return {"success": False, "message": "找不到域名记录"} + + # 验证权限 + if domain.team != team.name: + return {"success": False, "message": "您没有权限操作此域名"} + + # 更新自动续费状态 + domain.auto_renew = bool(auto_renew) + domain.save(ignore_permissions=True) + + return {"success": True, "message": "自动续费状态更新成功"} + + except Exception as e: + jingrow.log_error("域名管理", f"切换自动续费状态失败: {str(e)}") + return {"success": False, "message": f"操作失败: {str(e)}"} + + +@jingrow.whitelist() +def toggle_domain_whois_protection(pagetype, name, whois_protection): + """切换域名隐私保护状态""" + try: + # 获取当前用户团队 + team = get_current_team(True) + + # 获取域名记录 + domain = jingrow.get_pg(pagetype, name) + if not domain: + return {"success": False, "message": "找不到域名记录"} + + # 验证权限 + if domain.team != team.name: + return {"success": False, "message": "您没有权限操作此域名"} + + # 更新隐私保护状态 + domain.whois_protection = bool(whois_protection) + domain.save(ignore_permissions=True) + + return {"success": True, "message": "隐私保护状态更新成功"} + + except Exception as e: + jingrow.log_error("域名管理", f"切换隐私保护状态失败: {str(e)}") + return {"success": False, "message": f"操作失败: {str(e)}"} + + +@jingrow.whitelist() +def delete_domain(pagetype, name): + """删除域名记录""" + try: + # 获取当前用户团队 + team = get_current_team(True) + + # 获取域名记录 + domain = jingrow.get_pg(pagetype, name) + if not domain: + return {"success": False, "message": "找不到域名记录"} + + # 验证权限 + if domain.team != team.name: + return {"success": False, "message": "您没有权限操作此域名"} + + # 删除域名记录 + domain.delete(ignore_permissions=True) + + return {"success": True, "message": "域名记录删除成功"} + + except Exception as e: + jingrow.log_error("域名管理", f"删除域名记录失败: {str(e)}") + return {"success": False, "message": f"删除失败: {str(e)}"} + diff --git a/jcloud/jcloud/pagetype/jsite_domain/jsite_domain.py b/jcloud/jcloud/pagetype/jsite_domain/jsite_domain.py index df857ed..5c63e4f 100644 --- a/jcloud/jcloud/pagetype/jsite_domain/jsite_domain.py +++ b/jcloud/jcloud/pagetype/jsite_domain/jsite_domain.py @@ -1,7 +1,7 @@ # Copyright (c) 2025, Jingrow and contributors # For license information, please see license.txt -# import jingrow +import jingrow from jingrow.model.document import Document @@ -38,4 +38,36 @@ class JsiteDomain(Document): team: DF.Link | None whois_protection: DF.Check # end: auto-generated types - pass + + dashboard_fields = ( + "domain", + "status", + "domain_owner", + "domain_registrar", + "registration_date", + "end_date", + "price", + "period", + "auto_renew", + "team", + "order_id", + "description", + "whois_protection", + "admin_password", + "group", + "dns_host1", + "dns_host2", + "dns_host3", + "dns_host4", + "dns_host5", + "dns_host6" + ) + + @staticmethod + def get_list_query(query): + JsiteDomain = jingrow.qb.PageType("Jsite Domain") + query = query.where(JsiteDomain.team == jingrow.local.team().name) + return query.run(as_dict=True) + + def get_pg(self, pg): + return pg diff --git a/jcloud/jcloud/pagetype/order/order.json b/jcloud/jcloud/pagetype/order/order.json index a653186..dcea177 100644 --- a/jcloud/jcloud/pagetype/order/order.json +++ b/jcloud/jcloud/pagetype/order/order.json @@ -77,15 +77,15 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Order Type", - "options": "\n余额充值\n新建网站\n网站续费\n域名续费\n新建服务器\n服务器续费\n服务器升级", + "options": "\n余额充值\n新建网站\n网站续费\n域名注册\n域名续费\n新建服务器\n服务器续费\n服务器升级", "read_only": 1 } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-07-30 23:11:19.817353", - "modified_by": "Administrator", + "modified": "2025-08-01 00:44:25.874899", + "modified_by": "support@jingrow.com", "module": "Jcloud", "name": "Order", "owner": "Administrator", diff --git a/jcloud/jcloud/pagetype/order/order.py b/jcloud/jcloud/pagetype/order/order.py index 7400c2d..a0501ad 100644 --- a/jcloud/jcloud/pagetype/order/order.py +++ b/jcloud/jcloud/pagetype/order/order.py @@ -16,7 +16,7 @@ class Order(Document): description: DF.Data | None order_id: DF.Data | None - order_type: DF.Literal["", "\u4f59\u989d\u5145\u503c", "\u65b0\u5efa\u7f51\u7ad9", "\u7f51\u7ad9\u7eed\u8d39", "\u57df\u540d\u7eed\u8d39", "\u65b0\u5efa\u670d\u52a1\u5668", "\u670d\u52a1\u5668\u7eed\u8d39", "\u670d\u52a1\u5668\u5347\u7ea7"] + order_type: DF.Literal["", "\u4f59\u989d\u5145\u503c", "\u65b0\u5efa\u7f51\u7ad9", "\u7f51\u7ad9\u7eed\u8d39", "\u57df\u540d\u6ce8\u518c", "\u57df\u540d\u7eed\u8d39", "\u65b0\u5efa\u670d\u52a1\u5668", "\u670d\u52a1\u5668\u7eed\u8d39", "\u670d\u52a1\u5668\u5347\u7ea7"] payment_method: DF.Literal["", "\u652f\u4ed8\u5b9d", "\u5fae\u4fe1\u652f\u4ed8", "\u4f59\u989d\u652f\u4ed8", "\u94f6\u884c\u8f6c\u8d26", "\u5176\u4ed6"] status: DF.Literal["\u5f85\u652f\u4ed8", "\u5df2\u652f\u4ed8", "\u4ea4\u6613\u6210\u529f", "\u5df2\u53d6\u6d88", "\u5df2\u9000\u6b3e"] team: DF.Link | None