后台管理优化
This commit is contained in:
parent
7408d04140
commit
627ae2faa4
|
|
@ -46,6 +46,8 @@ CREATE TABLE t_license_record (
|
|||
cpu_serial VARCHAR(200) COMMENT '绑定CPU序列号',
|
||||
main_board_serial VARCHAR(200) COMMENT '绑定主板序列号',
|
||||
description VARCHAR(500) COMMENT '证书描述',
|
||||
license_content LONGTEXT COMMENT 'License证书文件内容(Base64编码)',
|
||||
public_key_content LONGTEXT COMMENT '公钥文件内容(Base64编码)',
|
||||
status INT NOT NULL DEFAULT 0 COMMENT '状态: 0-正常, 1-到期, 2-冻结',
|
||||
create_time DATETIME COMMENT '创建时间',
|
||||
update_time DATETIME COMMENT '更新时间',
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@ import org.springframework.beans.factory.annotation.Value;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @author lcz
|
||||
|
|
@ -121,4 +120,48 @@ public class LicenseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包下载证书和公钥文件
|
||||
*/
|
||||
@GetMapping("/downloadZip")
|
||||
public void downloadZip(@RequestParam(value = "name", required = false, defaultValue = "license") String packageName, HttpServletResponse response) throws IOException {
|
||||
File licenseFile = new File(keyStorePath + "license.lic");
|
||||
File publicKeyFile = new File(keyStorePath + "publicCerts.keystore");
|
||||
|
||||
if (!licenseFile.exists()) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "license.lic 文件不存在");
|
||||
return;
|
||||
}
|
||||
if (!publicKeyFile.exists()) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "publicCerts.keystore 文件不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + packageName + ".zip\"");
|
||||
|
||||
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
|
||||
FileInputStream licenseFis = new FileInputStream(licenseFile);
|
||||
FileInputStream publicKeyFis = new FileInputStream(publicKeyFile)) {
|
||||
|
||||
// 添加 license.lic 到 zip
|
||||
zos.putNextEntry(new ZipEntry("license.lic"));
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = licenseFis.read(buffer)) != -1) {
|
||||
zos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
zos.closeEntry();
|
||||
|
||||
// 添加 publicCerts.keystore 到 zip
|
||||
zos.putNextEntry(new ZipEntry("publicCerts.keystore"));
|
||||
while ((bytesRead = publicKeyFis.read(buffer)) != -1) {
|
||||
zos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
zos.closeEntry();
|
||||
} catch (IOException e) {
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "打包下载失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.lcz.licence.controller.manager;
|
|||
|
||||
import com.lcz.licence.entity.manager.Customer;
|
||||
import com.lcz.licence.service.manager.CustomerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
|
@ -13,12 +15,16 @@ import java.util.Map;
|
|||
@RequestMapping("/api/customers")
|
||||
public class CustomerController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CustomerController.class);
|
||||
|
||||
@Autowired
|
||||
private CustomerService customerService;
|
||||
|
||||
@GetMapping
|
||||
public Map<String, Object> list(@RequestParam(required = false) String keyword) {
|
||||
log.info("GET /api/customers - keyword: {}", keyword);
|
||||
List<Customer> list = customerService.search(keyword);
|
||||
log.info("GET /api/customers - 返回 {} 条数据", list.size());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("data", list);
|
||||
|
|
@ -28,22 +34,38 @@ public class CustomerController {
|
|||
|
||||
@GetMapping("/{id}")
|
||||
public Map<String, Object> get(@PathVariable Long id) {
|
||||
log.info("GET /api/customers/{}", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
java.util.Optional<Customer> optCustomer = customerService.findById(id);
|
||||
if (optCustomer.isPresent()) {
|
||||
result.put("code", 200);
|
||||
result.put("data", optCustomer.get());
|
||||
log.info("GET /api/customers/{} - 找到客户: {}", id, optCustomer.get().getName());
|
||||
} else {
|
||||
result.put("code", 404);
|
||||
result.put("msg", "客户不存在");
|
||||
log.warn("GET /api/customers/{} - 客户不存在", id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Map<String, Object> save(@RequestBody Customer customer) {
|
||||
log.info("POST /api/customers - 保存客户: {}", customer.getName());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
// 检查唯一性
|
||||
Map<String, String> duplicates = customerService.checkDuplicate(customer);
|
||||
if (!duplicates.isEmpty()) {
|
||||
log.warn("POST /api/customers - 存在重复数据: {}", duplicates);
|
||||
result.put("code", 400);
|
||||
result.put("msg", "存在重复数据");
|
||||
result.put("duplicates", duplicates);
|
||||
return result;
|
||||
}
|
||||
|
||||
Customer saved = customerService.save(customer);
|
||||
log.info("POST /api/customers - 保存成功, id: {}", saved.getId());
|
||||
result.put("code", 200);
|
||||
result.put("data", saved);
|
||||
result.put("msg", "保存成功");
|
||||
|
|
@ -52,9 +74,22 @@ public class CustomerController {
|
|||
|
||||
@PutMapping("/{id}")
|
||||
public Map<String, Object> update(@PathVariable Long id, @RequestBody Customer customer) {
|
||||
log.info("PUT /api/customers/{} - 更新客户: {}", id, customer.getName());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
customer.setId(id);
|
||||
|
||||
// 检查唯一性
|
||||
Map<String, String> duplicates = customerService.checkDuplicate(customer);
|
||||
if (!duplicates.isEmpty()) {
|
||||
log.warn("PUT /api/customers/{} - 存在重复数据: {}", id, duplicates);
|
||||
result.put("code", 400);
|
||||
result.put("msg", "存在重复数据");
|
||||
result.put("duplicates", duplicates);
|
||||
return result;
|
||||
}
|
||||
|
||||
Customer saved = customerService.save(customer);
|
||||
log.info("PUT /api/customers/{} - 更新成功", id);
|
||||
result.put("code", 200);
|
||||
result.put("data", saved);
|
||||
result.put("msg", "更新成功");
|
||||
|
|
@ -63,8 +98,10 @@ public class CustomerController {
|
|||
|
||||
@DeleteMapping("/{id}")
|
||||
public Map<String, Object> delete(@PathVariable Long id) {
|
||||
log.info("DELETE /api/customers/{}", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
customerService.delete(id);
|
||||
log.info("DELETE /api/customers/{} - 删除成功", id);
|
||||
result.put("code", 200);
|
||||
result.put("msg", "删除成功");
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,14 @@ package com.lcz.licence.controller.manager;
|
|||
|
||||
import com.lcz.licence.entity.manager.LicenseRecord;
|
||||
import com.lcz.licence.service.manager.LicenseRecordService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -13,12 +18,16 @@ import java.util.Map;
|
|||
@RequestMapping("/api/license-records")
|
||||
public class LicenseRecordController {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LicenseRecordController.class);
|
||||
|
||||
@Autowired
|
||||
private LicenseRecordService licenseRecordService;
|
||||
|
||||
@GetMapping
|
||||
public Map<String, Object> list() {
|
||||
log.info("GET /api/license-records - 查询所有License记录");
|
||||
List<LicenseRecord> list = licenseRecordService.findAll();
|
||||
log.info("GET /api/license-records - 返回 {} 条数据", list.size());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("code", 200);
|
||||
result.put("data", list);
|
||||
|
|
@ -28,22 +37,27 @@ public class LicenseRecordController {
|
|||
|
||||
@GetMapping("/{id}")
|
||||
public Map<String, Object> get(@PathVariable Long id) {
|
||||
log.info("GET /api/license-records/{}", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
java.util.Optional<LicenseRecord> optRecord = licenseRecordService.findById(id);
|
||||
if (optRecord.isPresent()) {
|
||||
result.put("code", 200);
|
||||
result.put("data", optRecord.get());
|
||||
log.info("GET /api/license-records/{} - 找到记录, subject: {}", id, optRecord.get().getSubject());
|
||||
} else {
|
||||
result.put("code", 404);
|
||||
result.put("msg", "记录不存在");
|
||||
log.warn("GET /api/license-records/{} - 记录不存在", id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Map<String, Object> save(@RequestBody LicenseRecord record) {
|
||||
log.info("POST /api/license-records - 保存License, customerId: {}, subject: {}", record.getCustomerId(), record.getSubject());
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
LicenseRecord saved = licenseRecordService.save(record);
|
||||
log.info("POST /api/license-records - 保存成功, id: {}", saved.getId());
|
||||
result.put("code", 200);
|
||||
result.put("data", saved);
|
||||
result.put("msg", "保存成功");
|
||||
|
|
@ -52,9 +66,11 @@ public class LicenseRecordController {
|
|||
|
||||
@PutMapping("/{id}")
|
||||
public Map<String, Object> update(@PathVariable Long id, @RequestBody LicenseRecord record) {
|
||||
log.info("PUT /api/license-records/{} - 更新License", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
record.setId(id);
|
||||
LicenseRecord saved = licenseRecordService.save(record);
|
||||
log.info("PUT /api/license-records/{} - 更新成功", id);
|
||||
result.put("code", 200);
|
||||
result.put("data", saved);
|
||||
result.put("msg", "更新成功");
|
||||
|
|
@ -63,8 +79,10 @@ public class LicenseRecordController {
|
|||
|
||||
@DeleteMapping("/{id}")
|
||||
public Map<String, Object> delete(@PathVariable Long id) {
|
||||
log.info("DELETE /api/license-records/{}", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
licenseRecordService.delete(id);
|
||||
log.info("DELETE /api/license-records/{} - 删除成功", id);
|
||||
result.put("code", 200);
|
||||
result.put("msg", "删除成功");
|
||||
return result;
|
||||
|
|
@ -77,14 +95,17 @@ public class LicenseRecordController {
|
|||
*/
|
||||
@PostMapping("/{id}/renew")
|
||||
public Map<String, Object> renew(@PathVariable Long id, @RequestParam(defaultValue = "12") int months) {
|
||||
log.info("POST /api/license-records/{}/renew - 续期 {} 个月", id, months);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
boolean success = licenseRecordService.renew(id, months);
|
||||
if (success) {
|
||||
result.put("code", 200);
|
||||
result.put("msg", "续期成功,延长" + months + "个月");
|
||||
log.info("POST /api/license-records/{}/renew - 续期成功", id);
|
||||
} else {
|
||||
result.put("code", 404);
|
||||
result.put("msg", "记录不存在");
|
||||
log.warn("POST /api/license-records/{}/renew - 记录不存在", id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -94,14 +115,17 @@ public class LicenseRecordController {
|
|||
*/
|
||||
@PostMapping("/{id}/freeze")
|
||||
public Map<String, Object> freeze(@PathVariable Long id) {
|
||||
log.info("POST /api/license-records/{}/freeze - 冻结", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
boolean success = licenseRecordService.freeze(id);
|
||||
if (success) {
|
||||
result.put("code", 200);
|
||||
result.put("msg", "已冻结");
|
||||
log.info("POST /api/license-records/{}/freeze - 冻结成功", id);
|
||||
} else {
|
||||
result.put("code", 404);
|
||||
result.put("msg", "记录不存在");
|
||||
log.warn("POST /api/license-records/{}/freeze - 记录不存在", id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -111,14 +135,17 @@ public class LicenseRecordController {
|
|||
*/
|
||||
@PostMapping("/{id}/unfreeze")
|
||||
public Map<String, Object> unfreeze(@PathVariable Long id) {
|
||||
log.info("POST /api/license-records/{}/unfreeze - 解冻", id);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
boolean success = licenseRecordService.unfreeze(id);
|
||||
if (success) {
|
||||
result.put("code", 200);
|
||||
result.put("msg", "已解冻");
|
||||
log.info("POST /api/license-records/{}/unfreeze - 解冻成功", id);
|
||||
} else {
|
||||
result.put("code", 404);
|
||||
result.put("msg", "记录不存在");
|
||||
log.warn("POST /api/license-records/{}/unfreeze - 记录不存在", id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -128,11 +155,98 @@ public class LicenseRecordController {
|
|||
*/
|
||||
@GetMapping("/expiring")
|
||||
public Map<String, Object> expiring() {
|
||||
log.info("GET /api/license-records/expiring - 查询即将到期的License");
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
List<LicenseRecord> list = licenseRecordService.findExpiringLicenses();
|
||||
log.info("GET /api/license-records/expiring - 返回 {} 条数据", list.size());
|
||||
result.put("code", 200);
|
||||
result.put("data", list);
|
||||
result.put("total", list.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载License证书文件
|
||||
*/
|
||||
@GetMapping("/{id}/downloadLicense")
|
||||
public void downloadLicense(@PathVariable Long id, HttpServletResponse response) throws IOException {
|
||||
log.info("GET /api/license-records/{}/downloadLicense - 下载License证书", id);
|
||||
java.util.Optional<LicenseRecord> optRecord = licenseRecordService.findById(id);
|
||||
if (!optRecord.isPresent()) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "记录不存在");
|
||||
return;
|
||||
}
|
||||
LicenseRecord record = optRecord.get();
|
||||
if (record.getLicenseContent() == null || record.getLicenseContent().length() == 0) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
|
||||
return;
|
||||
}
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"license.lic\"");
|
||||
byte[] bytes = Base64.getDecoder().decode(record.getLicenseContent());
|
||||
response.getOutputStream().write(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载公钥文件
|
||||
*/
|
||||
@GetMapping("/{id}/downloadPublicKey")
|
||||
public void downloadPublicKey(@PathVariable Long id, HttpServletResponse response) throws IOException {
|
||||
log.info("GET /api/license-records/{}/downloadPublicKey - 下载公钥文件", id);
|
||||
java.util.Optional<LicenseRecord> optRecord = licenseRecordService.findById(id);
|
||||
if (!optRecord.isPresent()) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "记录不存在");
|
||||
return;
|
||||
}
|
||||
LicenseRecord record = optRecord.get();
|
||||
if (record.getPublicKeyContent() == null || record.getPublicKeyContent().length() == 0) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
|
||||
return;
|
||||
}
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"publicCerts.keystore\"");
|
||||
byte[] bytes = Base64.getDecoder().decode(record.getPublicKeyContent());
|
||||
response.getOutputStream().write(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包下载License和公钥文件
|
||||
*/
|
||||
@GetMapping("/{id}/downloadZip")
|
||||
public void downloadZip(@PathVariable Long id, HttpServletResponse response) throws IOException {
|
||||
log.info("GET /api/license-records/{}/downloadZip - 打包下载", id);
|
||||
java.util.Optional<LicenseRecord> optRecord = licenseRecordService.findById(id);
|
||||
if (!optRecord.isPresent()) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "记录不存在");
|
||||
return;
|
||||
}
|
||||
LicenseRecord record = optRecord.get();
|
||||
|
||||
if ((record.getLicenseContent() == null || record.getLicenseContent().length() == 0) &&
|
||||
(record.getPublicKeyContent() == null || record.getPublicKeyContent().length() == 0)) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
response.setContentType("application/zip");
|
||||
String fileName = record.getSubject() != null ? record.getSubject() : "license";
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".zip\"");
|
||||
|
||||
java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(response.getOutputStream());
|
||||
|
||||
if (record.getLicenseContent() != null && record.getLicenseContent().length() > 0) {
|
||||
zos.putNextEntry(new java.util.zip.ZipEntry("license.lic"));
|
||||
zos.write(Base64.getDecoder().decode(record.getLicenseContent()));
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
if (record.getPublicKeyContent() != null && record.getPublicKeyContent().length() > 0) {
|
||||
zos.putNextEntry(new java.util.zip.ZipEntry("publicCerts.keystore"));
|
||||
zos.write(Base64.getDecoder().decode(record.getPublicKeyContent()));
|
||||
zos.closeEntry();
|
||||
}
|
||||
|
||||
zos.finish();
|
||||
zos.close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.lcz.licence.entity.manager;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
|
@ -55,10 +56,12 @@ public class Customer {
|
|||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||
private Date updateTime;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.lcz.licence.entity.manager;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
|
@ -46,11 +47,13 @@ public class LicenseRecord {
|
|||
/**
|
||||
* License 生效时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||
private Date issuedTime;
|
||||
|
||||
/**
|
||||
* License 到期时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||
private Date expireTime;
|
||||
|
||||
/**
|
||||
|
|
@ -93,6 +96,20 @@ public class LicenseRecord {
|
|||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* License证书文件内容(Base64编码)
|
||||
*/
|
||||
@Lob
|
||||
@Column(columnDefinition = "LONGTEXT")
|
||||
private String licenseContent;
|
||||
|
||||
/**
|
||||
* 公钥文件内容(Base64编码)
|
||||
*/
|
||||
@Lob
|
||||
@Column(columnDefinition = "LONGTEXT")
|
||||
private String publicKeyContent;
|
||||
|
||||
/**
|
||||
* 状态: 0-正常, 1-到期, 2-冻结
|
||||
*/
|
||||
|
|
@ -102,11 +119,13 @@ public class LicenseRecord {
|
|||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
|
||||
private Date updateTime;
|
||||
|
||||
public static final int STATUS_NORMAL = 0;
|
||||
|
|
|
|||
|
|
@ -10,4 +10,18 @@ import java.util.List;
|
|||
public interface CustomerRepository extends JpaRepository<Customer, Long> {
|
||||
|
||||
List<Customer> findByNameContaining(String name);
|
||||
|
||||
List<Customer> findAllByName(String name);
|
||||
|
||||
List<Customer> findAllByPhone(String phone);
|
||||
|
||||
List<Customer> findAllByEmail(String email);
|
||||
|
||||
long countByEmail(String email);
|
||||
|
||||
boolean existsByName(String name);
|
||||
|
||||
boolean existsByPhone(String phone);
|
||||
|
||||
boolean existsByEmail(String email);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.lcz.licence.service.manager;
|
|||
|
||||
import com.lcz.licence.entity.manager.Customer;
|
||||
import com.lcz.licence.repository.CustomerRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
|
@ -13,35 +15,100 @@ import java.util.Optional;
|
|||
@Service
|
||||
public class CustomerService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CustomerService.class);
|
||||
|
||||
@Autowired
|
||||
private CustomerRepository customerRepository;
|
||||
|
||||
public List<Customer> findAll() {
|
||||
return customerRepository.findAll();
|
||||
log.info("CustomerService.findAll - 查询所有客户");
|
||||
List<Customer> result = customerRepository.findAll();
|
||||
log.info("CustomerService.findAll - 查询到 {} 条数据", result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Customer> search(String keyword) {
|
||||
if (keyword == null || keyword.trim().isEmpty()) {
|
||||
log.info("CustomerService.search - keyword为空,执行全量查询");
|
||||
return customerRepository.findAll();
|
||||
}
|
||||
return customerRepository.findByNameContaining(keyword);
|
||||
log.info("CustomerService.search - keyword: {}", keyword);
|
||||
List<Customer> result = customerRepository.findByNameContaining(keyword);
|
||||
log.info("CustomerService.search - 模糊查询到 {} 条数据", result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
public Optional<Customer> findById(Long id) {
|
||||
log.info("CustomerService.findById - id: {}", id);
|
||||
return customerRepository.findById(id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Customer save(Customer customer) {
|
||||
log.info("CustomerService.save - 保存客户, name: {}, id: {}", customer.getName(), customer.getId());
|
||||
if (customer.getId() == null) {
|
||||
customer.setCreateTime(new Date());
|
||||
log.info("CustomerService.save - 新增客户,设置创建时间");
|
||||
}
|
||||
customer.setUpdateTime(new Date());
|
||||
return customerRepository.save(customer);
|
||||
Customer saved = customerRepository.save(customer);
|
||||
log.info("CustomerService.save - 保存成功, 返回id: {}", saved.getId());
|
||||
return saved;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(Long id) {
|
||||
log.info("CustomerService.delete - 删除客户, id: {}", id);
|
||||
customerRepository.deleteById(id);
|
||||
log.info("CustomerService.delete - 删除完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查唯一性,返回重复的字段名列表
|
||||
* @param customer 待检查的客户
|
||||
* @return 重复的字段列表(name, phone, email)
|
||||
*/
|
||||
public java.util.Map<String, String> checkDuplicate(Customer customer) {
|
||||
log.info("CustomerService.checkDuplicate - 检查客户: {}, id: {}", customer.getName(), customer.getId());
|
||||
java.util.Map<String, String> duplicates = new java.util.HashMap<>();
|
||||
|
||||
// 检查名称唯一性
|
||||
if (customer.getName() != null && !customer.getName().trim().isEmpty()) {
|
||||
List<Customer> existingList = customerRepository.findAllByName(customer.getName().trim());
|
||||
log.info("CustomerService.checkDuplicate - name查询到 {} 条数据", existingList.size());
|
||||
boolean hasDuplicate = existingList.stream()
|
||||
.anyMatch(c -> !c.getId().equals(customer.getId()));
|
||||
if (hasDuplicate) {
|
||||
log.warn("CustomerService.checkDuplicate - 发现重复name: {}", customer.getName());
|
||||
duplicates.put("name", "客户名称已存在");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查电话唯一性
|
||||
if (customer.getPhone() != null && !customer.getPhone().trim().isEmpty()) {
|
||||
List<Customer> existingList = customerRepository.findAllByPhone(customer.getPhone().trim());
|
||||
log.info("CustomerService.checkDuplicate - phone查询到 {} 条数据", existingList.size());
|
||||
boolean hasDuplicate = existingList.stream()
|
||||
.anyMatch(c -> !c.getId().equals(customer.getId()));
|
||||
if (hasDuplicate) {
|
||||
log.warn("CustomerService.checkDuplicate - 发现重复phone: {}", customer.getPhone());
|
||||
duplicates.put("phone", "电话号码已存在");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查邮箱唯一性
|
||||
if (customer.getEmail() != null && !customer.getEmail().trim().isEmpty()) {
|
||||
List<Customer> existingList = customerRepository.findAllByEmail(customer.getEmail().trim());
|
||||
log.info("CustomerService.checkDuplicate - email查询到 {} 条数据", existingList.size());
|
||||
boolean hasDuplicate = existingList.stream()
|
||||
.anyMatch(c -> !c.getId().equals(customer.getId()));
|
||||
if (hasDuplicate) {
|
||||
log.warn("CustomerService.checkDuplicate - 发现重复email: {}", customer.getEmail());
|
||||
duplicates.put("email", "邮箱地址已存在");
|
||||
}
|
||||
}
|
||||
|
||||
log.info("CustomerService.checkDuplicate - 检查完成, 重复字段: {}", duplicates.keySet());
|
||||
return duplicates;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,6 +452,15 @@
|
|||
<div class="download-desc">公钥文件</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="download-card" id="downloadZipBtn" style="background: rgba(99,102,241,0.05); border-color: #6366f1;">
|
||||
<div class="download-icon" style="background: linear-gradient(135deg, #6366f1, #4f46e5);">
|
||||
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 1a.5.5 0 0 1 .5.5v5.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L7.5 7.293V1.5A.5.5 0 0 1 8 1z"/><path d="M2 10.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="download-name">打包下载</div>
|
||||
<div class="download-desc">license.lic + publicCerts.keystore</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-close-modal" onclick="closeModal()">完成</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -491,6 +500,11 @@
|
|||
<div class="toast-container" id="toastContainer"></div>
|
||||
|
||||
<script>
|
||||
// 从URL读取客户ID
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const customerId = urlParams.get('customerId');
|
||||
let currentCustomer = null;
|
||||
|
||||
// 初始化日期 - 默认开始日期为今天,一年有效期
|
||||
(function initDates() {
|
||||
const now = new Date();
|
||||
|
|
@ -506,6 +520,16 @@
|
|||
document.getElementById('expireTime').value = fmt(nextYear);
|
||||
})();
|
||||
|
||||
// 如果有客户ID,获取客户信息并预填
|
||||
if (customerId) {
|
||||
fetch('/api/customers/' + customerId).then(r => r.json()).then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
currentCustomer = res.data;
|
||||
document.querySelector('input[name="subject"]').value = currentCustomer.name;
|
||||
}
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
document.getElementById('enableHardware').addEventListener('change', function () {
|
||||
document.getElementById('hardwareSection').style.display = this.checked ? 'block' : 'none';
|
||||
});
|
||||
|
|
@ -622,15 +646,91 @@
|
|||
body: JSON.stringify(data)
|
||||
}).then(r => r.json()).then(response => {
|
||||
if (response.code === "200") {
|
||||
// 如果有客户ID,保存License记录到数据库
|
||||
if (customerId) {
|
||||
saveLicenseRecord(data, response);
|
||||
}
|
||||
// 显示下载弹窗
|
||||
openModal();
|
||||
document.getElementById('downloadLicenseBtn').onclick = () => triggerDownload('/license/download?file=license.lic', 'license.lic', document.getElementById('downloadLicenseBtn'));
|
||||
document.getElementById('downloadPublicKeyBtn').onclick = () => triggerDownload('/license/download?file=publicCerts.keystore', 'publicCerts.keystore', document.getElementById('downloadPublicKeyBtn'));
|
||||
setupDownloadButtons();
|
||||
} else {
|
||||
showToast(response.msg || '生成失败', 'danger');
|
||||
}
|
||||
}).catch(() => showToast('网络请求失败', 'danger'));
|
||||
});
|
||||
|
||||
// 保存License记录到数据库
|
||||
function saveLicenseRecord(formData, response) {
|
||||
// formData.issuedTime 和 formData.expireTime 已经在表单提交时被转换过了
|
||||
const hwEnabled = document.getElementById('enableHardware').checked;
|
||||
|
||||
// 先获取文件内容
|
||||
Promise.all([
|
||||
fetch('/license/download?file=license.lic').then(r => r.blob()),
|
||||
fetch('/license/download?file=publicCerts.keystore').then(r => r.blob())
|
||||
]).then(([licenseBlob, publicKeyBlob]) => {
|
||||
// Blob 转 Base64
|
||||
const toBase64 = (blob) => new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result.split(',')[1]);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
|
||||
return Promise.all([toBase64(licenseBlob), toBase64(publicKeyBlob)]);
|
||||
}).then(([licenseBase64, publicKeyBase64]) => {
|
||||
const recordData = {
|
||||
customerId: parseInt(customerId),
|
||||
subject: formData.subject,
|
||||
issuedTime: formData.issuedTime,
|
||||
expireTime: formData.expireTime,
|
||||
consumerType: formData.consumerType,
|
||||
consumerAmount: parseInt(formData.consumerAmount) || 1,
|
||||
hardwareBindEnabled: hwEnabled,
|
||||
macAddress: hwEnabled ? (formData.additionInfo?.macAddress || '') : '',
|
||||
ipAddress: hwEnabled ? (formData.additionInfo?.ipAddress || '') : '',
|
||||
cpuSerial: hwEnabled ? (formData.additionInfo?.cpuSerial || '') : '',
|
||||
mainBoardSerial: hwEnabled ? (formData.additionInfo?.mainBoardSerial || '') : '',
|
||||
description: formData.description || '',
|
||||
status: 0,
|
||||
licenseContent: licenseBase64,
|
||||
publicKeyContent: publicKeyBase64
|
||||
};
|
||||
|
||||
return fetch('/api/license-records', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(recordData)
|
||||
}).then(r => r.json());
|
||||
}).then(res => {
|
||||
if (res.code === 200) {
|
||||
showToast('证书已生成并保存到数据库', 'success');
|
||||
} else {
|
||||
console.error('保存License记录失败:', res.msg);
|
||||
showToast('保存失败: ' + (res.msg || ''), 'error');
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('保存License记录失败:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// 设置下载按钮
|
||||
function setupDownloadButtons() {
|
||||
document.getElementById('downloadLicenseBtn').onclick = () => triggerDownload('/license/download?file=license.lic', 'license.lic', document.getElementById('downloadLicenseBtn'));
|
||||
document.getElementById('downloadPublicKeyBtn').onclick = () => triggerDownload('/license/download?file=publicCerts.keystore', 'publicCerts.keystore', document.getElementById('downloadPublicKeyBtn'));
|
||||
document.getElementById('downloadZipBtn').onclick = () => {
|
||||
const card = document.getElementById('downloadZipBtn');
|
||||
const subject = document.querySelector('input[name="subject"]').value || 'license';
|
||||
fetch('/license/downloadZip?name=' + encodeURIComponent(subject)).then(r => r.blob()).then(blob => {
|
||||
const a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.setAttribute('download', subject + '.zip');
|
||||
a.click();
|
||||
card.classList.add('downloaded');
|
||||
card.querySelector('.download-name').textContent = '打包下载 - 已下载';
|
||||
}).catch(() => showToast('打包下载失败', 'danger'));
|
||||
};
|
||||
}
|
||||
|
||||
function triggerDownload(url, filename, cardEl) {
|
||||
fetch(url).then(r => r.blob()).then(blob => {
|
||||
const a = document.createElement('a');
|
||||
|
|
@ -656,6 +756,10 @@
|
|||
|
||||
function closeModal() {
|
||||
document.getElementById('downloadModal').classList.remove('show');
|
||||
// 如果有客户ID,跳转到管理后台并刷新
|
||||
if (customerId) {
|
||||
window.location.href = '/license-admin.html';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('downloadModal').addEventListener('click', function(e) {
|
||||
|
|
|
|||
|
|
@ -150,7 +150,6 @@
|
|||
<div class="header">
|
||||
<h1>License 管理后台</h1>
|
||||
<nav class="header-nav">
|
||||
<a href="/generateLicense.html">生成证书</a>
|
||||
<a href="/license-admin.html" class="active">管理后台</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -228,16 +227,29 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">联系电话</label>
|
||||
<input type="text" class="form-control" id="customerPhone">
|
||||
<input type="text" class="form-control" id="customerPhone" onblur="validatePhoneInput(this)">
|
||||
<span class="field-error" id="phoneError" style="color:#ef4444;font-size:0.75rem;display:none;">请输入正确的电话号码</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">邮箱</label>
|
||||
<input type="email" class="form-control" id="customerEmail">
|
||||
<input type="email" class="form-control" id="customerEmail" onblur="validateEmailInput(this)">
|
||||
<span class="field-error" id="emailError" style="color:#ef4444;font-size:0.75rem;display:none;">请输入正确的邮箱地址</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">地址</label>
|
||||
<input type="text" class="form-control" id="customerAddress">
|
||||
<div style="display:flex;gap:0.5rem;">
|
||||
<select class="form-control" id="province" onchange="loadCities()" style="flex:1;">
|
||||
<option value="">请选择省份</option>
|
||||
</select>
|
||||
<select class="form-control" id="city" onchange="loadDistricts()" style="flex:1;">
|
||||
<option value="">请选择城市</option>
|
||||
</select>
|
||||
<select class="form-control" id="district" style="flex:1;">
|
||||
<option value="">请选择区县</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="customerAddress" placeholder="详细地址" style="margin-top:0.5rem;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">备注</label>
|
||||
|
|
@ -448,7 +460,7 @@
|
|||
|
||||
let licenseInfo = '-';
|
||||
let statusBadge = '-';
|
||||
let actions = `<button class="btn btn-primary btn-sm" onclick="openLicenseModal(${c.id})">创建License</button>`;
|
||||
let actions = `<button class="btn btn-primary btn-sm" onclick="openGeneratePage(${c.id})">生成证书</button>`;
|
||||
|
||||
if (latestLicense) {
|
||||
licenseInfo = latestLicense.subject || '-';
|
||||
|
|
@ -460,6 +472,7 @@
|
|||
: `<button class="btn btn-warning btn-sm" onclick="freezeLicense(${latestLicense.id})">冻结</button>`
|
||||
}
|
||||
<button class="btn btn-primary btn-sm" onclick="editLicense(${latestLicense.id})">编辑</button>
|
||||
<button class="btn btn-primary btn-sm" onclick="downloadLicenseZip(${latestLicense.id})">下载证书</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="deleteLicense(${latestLicense.id})">删除</button>
|
||||
`;
|
||||
}
|
||||
|
|
@ -489,6 +502,12 @@
|
|||
document.getElementById('customerForm').reset();
|
||||
document.getElementById('customerId').value = '';
|
||||
document.getElementById('customerModalTitle').textContent = '新建客户';
|
||||
// 清除错误样式
|
||||
document.getElementById('customerName').style.borderColor = '';
|
||||
document.getElementById('customerPhone').style.borderColor = '';
|
||||
document.getElementById('customerEmail').style.borderColor = '';
|
||||
document.getElementById('phoneError').style.display = 'none';
|
||||
document.getElementById('emailError').style.display = 'none';
|
||||
if (id) {
|
||||
const c = customers.find(x => x.id === id);
|
||||
if (c) {
|
||||
|
|
@ -497,7 +516,7 @@
|
|||
document.getElementById('customerContact').value = c.contact || '';
|
||||
document.getElementById('customerPhone').value = c.phone || '';
|
||||
document.getElementById('customerEmail').value = c.email || '';
|
||||
document.getElementById('customerAddress').value = c.address || '';
|
||||
setAddressFields(c.address || '');
|
||||
document.getElementById('customerRemark').value = c.remark || '';
|
||||
document.getElementById('customerModalTitle').textContent = '编辑客户';
|
||||
}
|
||||
|
|
@ -518,7 +537,7 @@
|
|||
contact: document.getElementById('customerContact').value,
|
||||
phone: phone,
|
||||
email: email,
|
||||
address: document.getElementById('customerAddress').value,
|
||||
address: getFullAddress(),
|
||||
remark: document.getElementById('customerRemark').value
|
||||
};
|
||||
if (!data.name) { showToast('请填写客户名称', 'error'); return; }
|
||||
|
|
@ -537,6 +556,25 @@
|
|||
showToast(id ? '更新成功' : '创建成功');
|
||||
closeCustomerModal();
|
||||
loadData();
|
||||
} else if (res.code === 400 && res.duplicates) {
|
||||
// 处理重复数据
|
||||
const dupFields = Object.keys(res.duplicates);
|
||||
let msg = dupFields.map(f => res.duplicates[f]).join(', ');
|
||||
showToast(msg, 'error');
|
||||
// 高亮重复字段
|
||||
if (dupFields.includes('name')) {
|
||||
document.getElementById('customerName').style.borderColor = '#ef4444';
|
||||
}
|
||||
if (dupFields.includes('phone')) {
|
||||
document.getElementById('phoneError').textContent = '电话号码已存在';
|
||||
document.getElementById('phoneError').style.display = 'block';
|
||||
document.getElementById('customerPhone').style.borderColor = '#ef4444';
|
||||
}
|
||||
if (dupFields.includes('email')) {
|
||||
document.getElementById('emailError').textContent = '邮箱地址已存在';
|
||||
document.getElementById('emailError').style.display = 'block';
|
||||
document.getElementById('customerEmail').style.borderColor = '#ef4444';
|
||||
}
|
||||
} else {
|
||||
showToast(res.msg || '操作失败', 'error');
|
||||
}
|
||||
|
|
@ -687,6 +725,34 @@
|
|||
else { showToast(res.msg || '删除失败', 'error'); }
|
||||
}
|
||||
|
||||
function openGeneratePage(customerId) {
|
||||
// 跳转到生成证书页面,并带上客户ID
|
||||
window.location.href = '/generateLicense.html?customerId=' + customerId;
|
||||
}
|
||||
|
||||
function downloadLicenseZip(id) {
|
||||
fetch(`/api/license-records/${id}`).then(r => r.json()).then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
const subject = res.data.subject || 'license';
|
||||
fetch(`/api/license-records/${id}/downloadZip`).then(r => {
|
||||
if (r.ok) {
|
||||
r.blob().then(blob => {
|
||||
const a = document.createElement('a');
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.setAttribute('download', subject + '.zip');
|
||||
a.click();
|
||||
showToast('下载成功');
|
||||
});
|
||||
} else {
|
||||
showToast('文件不存在或已被删除', 'error');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showToast('记录不存在', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function validatePhone(phone) {
|
||||
if (!phone) return true; // 空为可选
|
||||
// 中国大陆手机号:1开头的11位数字
|
||||
|
|
@ -696,7 +762,215 @@
|
|||
return phoneRegex.test(phone) || telRegex.test(phone);
|
||||
}
|
||||
|
||||
function validatePhoneInput(input) {
|
||||
const value = input.value.trim();
|
||||
const errorEl = document.getElementById('phoneError');
|
||||
if (value && !validatePhone(value)) {
|
||||
errorEl.style.display = 'block';
|
||||
input.style.borderColor = '#ef4444';
|
||||
} else {
|
||||
errorEl.style.display = 'none';
|
||||
input.style.borderColor = '';
|
||||
}
|
||||
}
|
||||
|
||||
function validateEmailInput(input) {
|
||||
const value = input.value.trim();
|
||||
const errorEl = document.getElementById('emailError');
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (value && !emailRegex.test(value)) {
|
||||
errorEl.style.display = 'block';
|
||||
input.style.borderColor = '#ef4444';
|
||||
} else {
|
||||
errorEl.style.display = 'none';
|
||||
input.style.borderColor = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 输入时清除错误样式
|
||||
document.getElementById('customerPhone').addEventListener('input', function() {
|
||||
this.style.borderColor = '';
|
||||
document.getElementById('phoneError').style.display = 'none';
|
||||
});
|
||||
document.getElementById('customerEmail').addEventListener('input', function() {
|
||||
this.style.borderColor = '';
|
||||
document.getElementById('emailError').style.display = 'none';
|
||||
});
|
||||
|
||||
// 省市区数据(简化版)
|
||||
const addressData = {
|
||||
"北京市": { "市辖区": ["东城区", "西城区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区", "怀柔区", "平谷区", "密云区", "延庆区"] },
|
||||
"天津市": { "市辖区": ["和平区", "河东区", "河西区", "南开区", "河北区", "红桥区", "东丽区", "西青区", "津南区", "北辰区", "武清区", "宝坻区", "滨海新区", "宁河区", "静海区", "蓟州区"] },
|
||||
"河北省": { "石家庄市": ["长安区", "桥西区", "新华区", "井陉矿区", "裕华区", "藁城区", "鹿泉区", "栾城区", "井陉县", "正定县", "行唐县", "灵寿县", "高邑县", "深泽县", "赞皇县", "无极县", "平山县", "元氏县", "赵县", "辛集市", "晋州市", "新乐市"], "唐山市": ["路南区", "路北区", "古冶区", "开平区", "丰南区", "丰润区", "曹妃甸区", "滦南县", "乐亭县", "迁西县", "玉田县", "遵化市", "迁安市", "滦州市"], "秦皇岛市": ["海港区", "山海关区", "北戴河区", "抚宁区", "青龙满族自治县", "昌黎县", "卢龙县"], "邯郸市": ["邯郸区", "丛台区", "复兴区", "峰峰矿区", "肥乡区", "永年区", "临漳县", "成安县", "大名县", "涉县", "磁县", "邱县", "鸡泽县", "广平县", "馆陶县", "魏县", "曲周县", "武安市"], "邢台市": ["襄都区", "信都区", "任泽区", "南和区", "临城县", "内丘县", "柏乡县", "隆尧县", "宁晋县", "巨鹿县", "新河县", "广宗县", "平乡县", "威县", "清河县", "临西县", "南宫市", "沙河市"], "保定市": ["竞秀区", "莲池区", "满城区", "清苑区", "徐水区", "涞水县", "阜平县", "定兴县", "唐县", "高阳县", "容城县", "涞源县", "望都县", "安新县", "易县", "曲阳县", "蠡县", "顺平县", "博野县", "雄县", "涿州市", "定州市", "安国市", "高碑店市"], "张家口市": ["桥东区", "桥西区", "宣化区", "下花园区", "万全区", "崇礼区", "张北县", "康保县", "沽源县", "尚义县", "蔚县", "阳原县", "怀安县", "怀来县", "涿鹿县", "赤城县"], "承德市": ["双桥区", "双滦区", "鹰手营子矿区", "承德县", "兴隆县", "滦平县", "隆化县", "丰宁满族自治县", "宽城满族自治县", "围场满族蒙古族自治县", "平泉市"], "沧州市": ["新华区", "运河区", "沧县", "青县", "东光县", "海兴县", "盐山县", "肃宁县", "南皮县", "吴桥县", "献县", "孟村回族自治县", "泊头市", "任丘市", "黄骅市", "河间市"], "廊坊市": ["安次区", "广阳区", "固安县", "永清县", "香河县", "大城县", "文安县", "大厂回族自治县", "霸州市", "三河市"], "衡水市": ["桃城区", "冀州区", "枣强县", "武邑县", "武强县", "饶阳县", "安平县", "故城县", "景县", "阜城县", "深州市"] },
|
||||
"山西省": { "太原市": ["小店区", "迎泽区", "杏花岭区", "尖草坪区", "万柏林区", "晋源区", "清徐县", "阳曲县", "娄烦县", "古交市"], "大同市": ["新荣区", "平城区", "云冈区", "云州区", "阳高县", "天镇县", "广灵县", "灵丘县", "浑源县", "左云县"], "阳泉市": ["城区", "矿区", "郊区", "平定县", "盂县"], "长治市": ["潞州区", "上党区", "屯留区", "潞城区", "襄垣县", "平顺县", "黎城县", "壶关县", "长子县", "武乡县", "沁县", "沁源县"], "晋城市": ["城区", "沁水县", "阳城县", "陵川县", "泽州县", "高平市"], "朔州市": ["朔城区", "平鲁区", "山阴县", "应县", "右玉县", "怀仁市"], "晋中市": ["榆次区", "太谷区", "榆社县", "左权县", "和顺县", "昔阳县", "寿阳县", "祁县", "平遥县", "灵石县", "介休市"], "运城市": ["盐湖区", "临猗县", "万荣县", "闻喜县", "稷山县", "新绛县", "绛县", "垣曲县", "夏县", "平陆县", "芮城县", "永济市", "河津市"], "忻州市": ["忻府区", "定襄县", "五台县", "代县", "繁峙县", "宁武县", "静乐县", "神池县", "五寨县", "岢岚县", "河曲县", "保德县", "偏关县", "原平市"], "临汾市": ["尧都区", "曲沃县", "翼城县", "襄汾县", "洪洞县", "古县", "安泽县", "浮山县", "吉县", "乡宁县", "大宁县", "隰县", "永和县", "蒲县", "汾西县", "侯马市", "霍州市"], "吕梁市": ["离石区", "文水县", "交城县", "兴县", "临县", "柳林县", "石楼县", "岚县", "方山县", "中阳县", "交口县", "孝义市", "汾阳市"] },
|
||||
"内蒙古自治区": { "呼和浩特市": ["新城区", "回民区", "玉泉区", "赛罕区", "土默特左旗", "托克托县", "和林格尔县", "清水河县", "武川县"], "包头市": ["东河区", "昆都仑区", "青山区", "石拐区", "白云鄂博矿区", "九原区", "土默特右旗", "固阳县", "达尔罕茂明安联合旗"], "乌海市": ["海勃湾区", "海南区", "乌达区"], "赤峰市": ["红山区", "元宝山区", "松山区", "阿鲁科尔沁旗", "巴林左旗", "巴林右旗", "林西县", "克什克腾旗", "翁牛特旗", "喀喇沁旗", "宁城县", "敖汉旗"], "通辽市": ["科尔沁区", "科尔沁左翼中旗", "科尔沁左翼后旗", "开鲁县", "库伦旗", "奈曼旗", "扎鲁特旗", "霍林郭勒市"], "鄂尔多斯市": ["东胜区", "康巴什区", "达拉特旗", "准格尔旗", "鄂托克前旗", "鄂托克旗", "杭锦旗", "乌审旗", "伊金霍洛旗"], "呼伦贝尔市": ["海拉尔区", "扎赉诺尔区", "阿荣旗", "莫力达瓦达斡尔族自治旗", "鄂伦春自治旗", "鄂温克族自治旗", "陈巴尔虎旗", "新巴尔虎左旗", "新巴尔虎右旗", "满洲里市", "牙克石市", "扎兰屯市", "额尔古纳市", "根河市"], "巴彦淖尔市": ["临河区", "五原县", "磴口县", "乌拉特前旗", "乌拉特中旗", "乌拉特后旗", "杭锦后旗"], "乌兰察布市": ["集宁区", "卓资县", "化德县", "商都县", "兴和县", "凉城县", "察哈尔右翼前旗", "察哈尔右翼中旗", "察哈尔右翼后旗", "四子王旗", "丰镇市"], "兴安盟": ["乌兰浩特市", "阿尔山市", "科尔沁右翼前旗", "科尔沁右翼中旗", "扎赉特旗", "突泉县"], "锡林郭勒盟": ["锡林浩特市", "阿巴嘎旗", "苏尼特左旗", "苏尼特右旗", "东乌珠穆沁旗", "西乌珠穆沁旗", "太仆寺旗", "镶黄旗", "正镶白旗", "正蓝旗", "多伦县"], "阿拉善盟": ["阿拉善左旗", "阿拉善右旗", "额济纳旗"] },
|
||||
"辽宁省": { "沈阳市": ["和平区", "沈河区", "大东区", "皇姑区", "铁西区", "苏家屯区", "浑南区", "沈北新区", "于洪区", "辽中区", "康平县", "法库县", "新民市"], "大连市": ["中山区", "西岗区", "沙河口区", "甘井子区", "旅顺口区", "金州区", "普兰店区", "长海县", "瓦房店市", "庄河市"], "鞍山市": ["铁东区", "铁西区", "立山区", "千山区", "台安县", "岫岩满族自治县", "海城市"], "抚顺市": ["新抚区", "东洲区", "望花区", "顺城区", "抚顺县", "新宾满族自治县", "清原满族自治县"], "本溪市": ["平山区", "溪湖区", "明山区", "南芬区", "本溪满族自治县", "桓仁满族自治县"], "丹东市": ["元宝区", "振兴区", "振安区", "宽甸满族自治县", "东港市", "凤城市"], "锦州市": ["古塔区", "凌河区", "太和区", "黑山县", "义县", "凌海市", "北镇市"], "营口市": ["站前区", "西市区", "鲅鱼圈区", "老边区", "盖州市", "大石桥市"], "阜新市": ["海州区", "新邱区", "太平区", "清河门区", "细河区", "阜新蒙古族自治县", "彰武县"], "辽阳市": ["白塔区", "文圣区", "宏伟区", "弓长岭区", "太子河区", "辽阳县", "灯塔市"], "盘锦市": ["双台子区", "兴隆台区", "大洼区", "盘山县"], "铁岭市": ["银州区", "清河区", "铁岭县", "西丰县", "昌图县", "调兵山市", "开原市"], "朝阳市": ["双塔区", "龙城区", "朝阳县", "建平县", "喀喇沁左翼蒙古族自治县", "北票市", "凌源市"], "葫芦岛市": ["连山区", "龙港区", "南票区", "绥中县", "建昌县", "兴城市"] },
|
||||
"吉林省": { "长春市": ["南关区", "宽城区", "朝阳区", "二道区", "绿园区", "双阳区", "九台区", "农安县", "德惠市"], "吉林市": ["昌邑区", "龙潭区", "船营区", "丰满区", "永吉县", "蛟河市", "桦甸市", "舒兰市", "磐石市"], "四平市": ["铁西区", "铁东区", "梨树县", "伊通满族自治县", "公主岭市", "双辽市"], "辽源市": ["龙山区", "西安区", "东丰县", "东辽县"], "通化市": ["东昌区", "二道江区", "通化县", "辉南县", "柳河县", "梅河口市", "集安市"], "白山市": ["浑江区", "江源区", "抚松县", "靖宇县", "长白朝鲜族自治县", "临江市"], "松原市": ["宁江区", "前郭尔罗斯蒙古族自治县", "长岭县", "乾安县", "扶余市"], "白城市": ["洮北区", "镇赉县", "通榆县", "洮南市", "大安市"], "延边朝鲜族自治州": ["延吉市", "图们市", "敦化市", "珲春市", "龙井市", "和龙市", "汪清县", "安图县"] },
|
||||
"黑龙江省": { "哈尔滨市": ["道里区", "南岗区", "道外区", "平房区", "松北区", "香坊区", "呼兰区", "阿城区", "双城区", "依兰县", "方正县", "宾县", "巴彦县", "木兰县", "通河县", "延寿县", "尚志市", "五常市"], "齐齐哈尔市": ["龙沙区", "建华区", "铁锋区", "昂昂溪区", "富拉尔基区", "碾子山区", "梅里斯达斡尔族区", "龙江县", "依安县", "泰来县", "甘南县", "富裕县", "克山县", "克东县", "拜泉县", "讷河市"], "鸡西市": ["鸡冠区", "恒山区", "滴道区", "梨树区", "城子河区", "麻山区", "鸡东县", "虎林市", "密山市"], "鹤岗市": ["向阳区", "工农区", "南山区", "兴安区", "东山区", "兴山区", "萝北县", "绥滨县"], "双鸭山市": ["尖山区", "岭东区", "四方台区", "宝山区", "集贤县", "友谊县", "宝清县", "饶河县"], "大庆市": ["萨尔图区", "龙凤区", "让胡路区", "红岗区", "大同区", "肇州县", "肇源县", "林甸县", "杜尔伯特蒙古族自治县"], "伊春市": ["伊美区", "乌翠区", "友好区", "嘉荫县", "汤旺县", "丰林县", "大箐山县", "南岔县", "金林区", "铁力市"], "佳木斯市": ["向阳区", "前进区", "东风区", "郊区", "桦南县", "桦川县", "汤原县", "同江市", "富锦市", "抚远市"], "七台河市": ["新兴区", "桃山区", "茄子河区", "勃利县"], "牡丹江市": ["东安区", "阳明区", "爱民区", "西安区", "林口县", "绥芬河市", "海林市", "宁安市", "穆棱市", "东宁市"], "黑河市": ["爱辉区", "逊克县", "孙吴县", "北安市", "五大连池市", "嫩江市"], "绥化市": ["北林区", "望奎县", "兰西县", "青冈县", "庆安县", "绥棱县", "安达市", "肇东市", "海伦市"], "大兴安岭地区": ["漠河市", "呼玛县", "塔河县"] },
|
||||
"上海市": { "市辖区": ["黄浦区", "徐汇区", "长宁区", "静安区", "普陀区", "虹口区", "杨浦区", "闵行区", "宝山区", "嘉定区", "浦东新区", "金山区", "松江区", "青浦区", "奉贤区", "崇明区"] },
|
||||
"江苏省": { "南京市": ["玄武区", "秦淮区", "建邺区", "鼓楼区", "浦口区", "栖霞区", "雨花台区", "江宁区", "六合区", "溧水区", "高淳区"], "无锡市": ["锡山区", "惠山区", "滨湖区", "梁溪区", "新吴区", "江阴市", "宜兴市"], "徐州市": ["鼓楼区", "云龙区", "贾汪区", "泉山区", "铜山区", "丰县", "沛县", "睢宁县", "新沂市", "邳州市"], "常州市": ["天宁区", "钟楼区", "新北区", "武进区", "金坛区", "溧阳市"], "苏州市": ["虎丘区", "吴中区", "相城区", "姑苏区", "吴江区", "苏州工业园区", "常熟市", "张家港市", "昆山市", "太仓市"], "南通市": ["崇川区", "通州区", "海门区", "如东县", "启东市", "如皋市", "海安市"], "连云港市": ["连云区", "海州区", "赣榆区", "东海县", "灌云县", "灌南县"], "淮安市": ["淮安区", "淮阴区", "清江浦区", "洪泽区", "涟水县", "盱眙县", "金湖县"], "盐城市": ["亭湖区", "盐都区", "大丰区", "响水县", "滨海县", "阜宁县", "射阳县", "建湖县", "东台市"], "扬州市": ["广陵区", "邗江区", "江都区", "宝应县", "仪征市", "高邮市"], "镇江市": ["京口区", "润州区", "丹徒区", "丹阳市", "扬中市", "句容市"], "泰州市": ["海陵区", "高港区", "姜堰区", "兴化市", "靖江市", "泰兴市"], "宿迁市": ["宿城区", "宿豫区", "沭阳县", "泗阳县", "泗洪县"] },
|
||||
"浙江省": { "杭州市": ["上城区", "下城区", "江干区", "拱墅区", "西湖区", "滨江区", "萧山区", "余杭区", "富阳区", "临安区", "桐庐县", "淳安县", "建德市", "临平区", "钱塘区"], "宁波市": ["海曙区", "江北区", "北仑区", "镇海区", "鄞州区", "奉化区", "象山县", "宁海县", "余姚市", "慈溪市"], "温州市": ["鹿城区", "龙湾区", "瓯海区", "洞头区", "永嘉县", "平阳县", "苍南县", "文成县", "泰顺县", "瑞安市", "乐清市", "龙港市"], "嘉兴市": ["南湖区", "秀洲区", "嘉善县", "海盐县", "海宁市", "平湖市", "桐乡市"], "湖州市": ["吴兴区", "南浔区", "德清县", "长兴县", "安吉县"], "绍兴市": ["越城区", "柯桥区", "上虞区", "新昌县", "诸暨市", "嵊州市"], "金华市": ["婺城区", "金东区", "武义县", "浦江县", "磐安县", "兰溪市", "义乌市", "东阳市", "永康市"], "衢州市": ["柯城区", "衢江区", "常山县", "开化县", "龙游县", "江山市"], "舟山市": ["定海区", "普陀区", "岱山县", "嵊泗县"], "台州市": ["椒江区", "黄岩区", "路桥区", "三门县", "天台县", "仙居县", "温岭市", "临海市", "玉环市"], "丽水市": ["莲都区", "青田县", "缙云县", "遂昌县", "松阳县", "云和县", "庆元县", "景宁畲族自治县", "龙泉市"] },
|
||||
"安徽省": { "合肥市": ["瑶海区", "庐阳区", "蜀山区", "包河区", "长丰县", "肥东县", "肥西县", "庐江县", "巢湖市"], "芜湖市": ["镜湖区", "弋江区", "鸠江区", "三山区", "芜湖县", "繁昌县", "南陵县", "无为市"], "蚌埠市": ["龙子湖区", "蚌山区", "禹会区", "淮上区", "怀远县", "五河县", "固镇县"], "淮南市": ["大通区", "田家庵区", "谢家集区", "八公山区", "潘集区", "凤台县", "寿县"], "马鞍山市": ["花山区", "雨山区", "博望区", "当涂县", "含山县", "和县"], "淮北市": ["杜集区", "相山区", "烈山区", "濉溪县"], "铜陵市": ["铜官区", "义安区", "郊区", "枞阳县"], "安庆市": ["迎江区", "大观区", "宜秀区", "怀宁县", "太湖县", "宿松县", "望江县", "岳西县", "桐城市", "潜山市"], "黄山市": ["屯溪区", "黄山区", "徽州区", "歙县", "休宁县", "黟县", "祁门县"], "滁州市": ["琅琊区", "南谯区", "来安县", "全椒县", "定远县", "凤阳县", "天长市", "明光市"], "阜阳市": ["颍州区", "颍东区", "颍泉区", "临泉县", "太和县", "阜南县", "颍上县", "界首市"], "宿州市": ["埇桥区", "砀山县", "萧县", "灵璧县", "泗县"], "六安市": ["金安区", "裕安区", "叶集区", "霍邱县", "舒城县", "金寨县", "霍山县"], "亳州市": ["谯城区", "涡阳县", "蒙城县", "利辛县"], "池州市": ["贵池区", "东至县", "石台县", "青阳县"], "宣城市": ["宣州区", "郎溪县", "泾县", "绩溪县", "旌德县", "宁国市", "广德市"] },
|
||||
"福建省": { "福州市": ["鼓楼区", "台江区", "仓山区", "马尾区", "晋安区", "长乐区", "闽侯县", "连江县", "罗源县", "闽清县", "永泰县", "平潭县", "福清市"], "厦门市": ["思明区", "海沧区", "湖里区", "集美区", "同安区", "翔安区"], "莆田市": ["城厢区", "涵江区", "荔城区", "秀屿区", "仙游县"], "三明市": ["三元区", "沙县区", "明溪县", "清流县", "宁化县", "大田县", "尤溪县", "将乐县", "泰宁县", "建宁县", "永安市"], "泉州市": ["鲤城区", "丰泽区", "洛江区", "泉港区", "惠安县", "安溪县", "永春县", "德化县", "金门县", "石狮市", "晋江市", "南安市"], "漳州市": ["芗城区", "龙文区", "龙海区", "长泰区", "云霄县", "漳浦县", "诏安县", "东山诏安", "南靖县", "平和县", "华安县"], "南平市": ["延平区", "建阳区", "顺昌县", "浦城县", "光泽县", "松溪县", "政和县", "邵武市", "武夷山市", "建瓯市"], "龙岩市": ["新罗区", "永定区", "长汀县", "上杭县", "武平县", "连城县", "漳平市"], "宁德市": ["蕉城区", "霞浦县", "古田县", "屏南县", "寿宁县", "周宁县", "柘荣县", "福安市", "福鼎市"] },
|
||||
"江西省": { "南昌市": ["东湖区", "西湖区", "青云谱区", "青山湖区", "新建区", "红谷滩区", "南昌县", "安义县", "进贤县"], "景德镇市": ["昌江区", "珠山区", "浮梁县", "乐平市"], "萍乡市": ["安源区", "湘东区", "莲花县", "上栗县", "芦溪县"], "九江市": ["濂溪区", "浔阳区", "柴桑区", "武宁县", "修水县", "永修县", "德安县", "都昌县", "湖口县", "彭泽县", "瑞昌市", "共青城市", "庐山市"], "新余市": ["渝水区", "分宜县"], "鹰潭市": ["月湖区", "余江区", "贵溪市"], "赣州市": ["章贡区", "南康区", "赣县区", "信丰县", "大余县", "上犹县", "崇义县", "安远县", "定南县", "全南县", "宁都县", "于都县", "兴国县", "会昌县", "寻乌县", "石城县", "瑞金市", "龙南市"], "吉安市": ["吉州区", "青原区", "吉安县", "吉水县", "峡江县", "新干县", "永丰县", "泰和县", "遂川县", "万安县", "安福县", "永新县", "井冈山市"], "宜春市": ["袁州区", "奉新县", "万载县", "上高县", "宜丰县", "靖安县", "铜鼓县", "丰城市", "樟树市", "高安市"], "抚州市": ["临川区", "东乡区", "南城县", "黎川县", "南丰县", "崇仁县", "乐安县", "宜黄县", "金溪县", "资溪县", "广昌县"], "上饶市": ["信州区", "广丰区", "广信区", "玉山县", "铅山县", "横峰县", "弋阳县", "余干县", "鄱阳县", "万年县", "婺源县", "德兴市"] },
|
||||
"山东省": { "济南市": ["历下区", "市中区", "槐荫区", "天桥区", "历城区", "长清区", "章丘区", "济阳区", "莱芜区", "钢城区", "平阴县", "商河县", "商河县", "青岛区"], "青岛市": ["市南区", "市北区", "黄岛区", "崂山区", "李沧区", "城阳区", "胶州市", "即墨区", "平度市", "莱西市", "西海岸新区"], "淄博市": ["淄川区", "张店区", "博山区", "临淄区", "周村区", "桓台县", "高青县", "沂源县"], "枣庄市": ["市中区", "薛城区", "峄城区", "台儿庄区", "山亭区", "滕州市"], "东营市": ["东营区", "河口区", "垦利区", "利津县", "广饶县"], "烟台市": ["芝罘区", "福山区", "牟平区", "莱山区", "蓬莱区", "龙口市", "莱阳市", "莱州市", "招远市", "栖霞市", "海阳市", "烟台开发区"], "潍坊市": ["潍城区", "寒亭区", "坊子区", "奎文区", "临朐县", "昌乐县", "青州市", "诸城市", "寿光市", "安丘市", "高密市", "昌邑市"], "济宁市": ["任城区", "兖州区", "微山县", "鱼台县", "金乡县", "嘉祥县", "汶上县", "泗水县", "梁山县", "曲阜市", "邹城市"], "泰安市": ["泰山区", "岱岳区", "宁阳县", "东平县", "新泰市", "肥城市"], "威海市": ["环翠区", "文登区", "荣成市", "乳山市", "威海经技区", "威海高区", "威海临港区"], "日照市": ["东港区", "岚山区", "五莲县", "莒县"], "临沂市": ["兰山区", "罗庄区", "河东区", "沂南县", "郯城县", "沂水县", "兰陵县", "费县", "平邑县", "莒南县", "蒙阴县", "临沭县"], "德州市": ["德城区", "陵城区", "宁津县", "庆云县", "临邑县", "齐河县", "平原县", "夏津县", "武城县", "乐陵市", "禹城市"], "聊城市": ["东昌府区", "茌平区", "阳谷县", "莘县", "东阿县", "冠县", "高唐县", "临清市"], "滨州市": ["滨城区", "沾化区", "惠民县", "阳信县", "无棣县", "博兴县", "邹平市"], "菏泽市": ["牡丹区", "定陶区", "曹县", "单县", "成武县", "巨野县", "郓城县", "鄄城县", "东明县"] },
|
||||
"河南省": { "郑州市": ["中原区", "二七区", "管城回族区", "金水区", "上街区", "惠济区", "中牟县", "巩义市", "荥阳市", "新密市", "新郑市", "登封市", "郑东新区"], "开封市": ["龙亭区", "顺河回族区", "鼓楼区", "禹王台区", "祥符区", "杞县", "通许县", "尉氏县", "兰考县"], "洛阳市": ["老城区", "西工区", "瀍河回族区", "涧西区", "吉利区", "洛龙区", "孟津区", "新安县", "栾川县", "嵩县", "汝阳县", "宜阳县", "洛宁县", "伊川县", "偃师区"], "平顶山市": ["新华区", "卫东区", "石龙区", "湛河区", "宝丰县", "叶县", "鲁山县", "郏县", "舞钢市", "汝州市"], "安阳市": ["文峰区", "北关区", "殷都区", "龙安区", "安阳县", "汤阴县", "滑县", "内黄县", "林州市"], "鹤壁市": ["鹤山区", "山城区", "淇滨区", "浚县", "淇县"], "新乡市": ["红旗区", "卫滨区", "凤泉区", "牧野区", "新乡县", "获嘉县", "原阳县", "延津县", "封丘县", "卫辉市", "辉县市", "长垣市"], "焦作市": ["解放区", "中站区", "马村区", "山阳区", "修武县", "博爱县", "武陟县", "温县", "沁阳市", "孟州市"], "濮阳市": ["华龙区", "清丰县", "南乐县", "范县", "台前县", "濮阳县"], "许昌市": ["魏都区", "建安区", "鄢陵县", "襄城县", "禹州市", "长葛市"], "漯河市": ["源汇区", "郾城区", "召陵区", "舞阳县", "临颍县"], "三门峡市": ["湖滨区", "陕州区", "渑池县", "卢氏县", "义马市", "灵宝市"], "南阳市": ["宛城区", "卧龙区", "南召县", "方城县", "西峡县", "镇平县", "内乡县", "淅川县", "社旗县", "唐河县", "新野县", "桐柏县", "邓州市"], "商丘市": ["梁园区", "睢阳区", "民权县", "睢县", "宁陵县", "柘城县", "虞城县", "夏邑县", "永城市"], "信阳市": ["浉河区", "平桥区", "罗山县", "光山县", "新县", "商城县", "固始县", "潢川县", "淮滨县", "息县"], "周口市": ["川汇区", "淮阳区", "扶沟县", "西华县", "商水县", "沈丘县", "鄢陵县", "项城市", "鹿邑县", "太康县"], "驻马店市": ["驿城区", "西平县", "上蔡县", "平舆县", "正阳县", "确山县", "泌阳县", "汝南县", "遂平县", "新蔡县"], "济源市": ["济源市"] },
|
||||
"湖北省": { "武汉市": ["江岸区", "江汉区", "硚口区", "汉阳区", "武昌区", "青山区", "洪山区", "东西湖区", "汉南区", "蔡甸区", "江夏区", "黄陂区", "新洲区"], "黄石市": ["黄石港区", "西塞山区", "下陆区", "铁山区", "阳新县", "大冶市"], "十堰市": ["茅箭区", "张湾区", "郧阳区", "郧西县", "竹山县", "竹溪县", "房县", "丹江口市"], "宜昌市": ["西陵区", "伍家岗区", "点军区", "猇亭区", "夷陵区", "远安县", "兴山县", "秭归县", "长阳土家族自治县", "五峰土家族自治县", "宜都市", "当阳市", "枝江市"], "襄阳市": ["襄城区", "樊城区", "襄州区", "南漳县", "谷城县", "保康县", "老河口市", "枣阳市", "宜城市"], "鄂州市": ["梁子湖区", "华容区", "鄂城区"], "荆门市": ["东宝区", "掇刀区", "沙洋县", "钟祥市", "京山市"], "孝感市": ["孝南区", "孝昌县", "大悟县", "云梦县", "应城市", "安陆市", "汉川市"], "荆州市": ["沙市区", "荆州区", "公安县", "监利县", "江陵县", "石首市", "洪湖市", "松滋市"], "黄冈市": ["黄州区", "团风县", "红安县", "罗田县", "英山县", "浠水县", "蕲春县", "黄梅县", "麻城市", "武穴市"], "咸宁市": ["咸安区", "嘉鱼县", "通城县", "崇阳县", "通山县", "赤壁市"], "随州市": ["曾都区", "随县", "广水市"], "恩施土家族苗族自治州": ["恩施市", "利川市", "建始县", "巴东县", "宣恩县", "咸丰县", "来凤县", "鹤峰县"], "仙桃市": ["仙桃市"], "潜江市": ["潜江市"], "天门市": ["天门市"], "神农架林区": ["神农架林区"] },
|
||||
"湖南省": { "长沙市": ["芙蓉区", "天心区", "岳麓区", "开福区", "雨花区", "望城区", "长沙县", "浏阳市", "宁乡市"], "株洲市": ["荷塘区", "芦淞区", "石峰区", "天元区", "渌口区", "攸县", "茶陵县", "炎陵县", "醴陵市"], "湘潭市": ["雨湖区", "岳塘区", "湘潭县", "湘乡市", "韶山市"], "衡阳市": ["珠晖区", "雁峰区", "石鼓区", "蒸湘区", "南岳区", "衡阳县", "衡南县", "衡山县", "衡东县", "祁东县", "耒阳市", "常宁市"], "邵阳市": ["双清区", "大祥区", "北塔区", "新邵县", "邵阳县", "隆回县", "洞口县", "绥宁县", "新宁县", "城步苗族自治县", "武冈市", "邵东市"], "岳阳市": ["岳阳楼区", "云溪区", "君山区", "岳阳县", "华容县", "湘阴县", "平江县", "汨罗市", "临湘市"], "常德市": ["武陵区", "鼎城区", "安乡县", "汉寿县", "澧县", "临澧县", "桃源县", "石门县", "津市市"], "张家界市": ["永定区", "武陵源区", "慈利县", "桑植县"], "益阳市": ["资阳区", "赫山区", "南县", "桃江县", "安化县", "沅江市"], "郴州市": ["北湖区", "苏仙区", "桂阳县", "宜章县", "永兴县", "嘉禾县", "临武县", "汝城县", "桂东县", "安仁县", "资兴市"], "永州市": ["零陵区", "冷水滩区", "祁阳县", "东安县", "双牌县", "道县", "江永县", "宁远县", "蓝山县", "新田县", "江华瑶族自治县", "回龙圩管理区"], "怀化市": ["鹤城区", "中方县", "沅陵县", "辰溪县", "溆浦县", "会同县", "麻阳苗族自治县", "新晃侗族自治县", "芷江侗族自治县", "靖州苗族侗族自治县", "通道侗族自治县", "洪江市"], "娄底市": ["娄星区", "双峰县", "新化县", "冷水江市", "涟源市"], "湘西土家族苗族自治州": ["吉首市", "泸溪县", "凤凰县", "花垣县", "保靖县", "古丈县", "永顺县", "龙山县"] },
|
||||
"广东省": { "广州市": ["荔湾区", "越秀区", "海珠区", "天河区", "白云区", "黄埔区", "番禺区", "花都区", "南沙区", "从化区", "增城区"], "韶关市": ["武江区", "浈江区", "曲江区", "始兴县", "仁化县", "翁源县", "乳源瑶族自治县", "新丰县", "乐昌市", "南雄市"], "深圳市": ["罗湖区", "福田区", "南山区", "宝安区", "龙岗区", "盐田区", "龙华区", "坪山区", "光明区", "大鹏新区"], "珠海市": ["香洲区", "斗门区", "金湾区", "横琴新区"], "汕头市": ["龙湖区", "金平区", "濠江区", "潮阳区", "潮南区", "澄海区", "南澳县"], "佛山市": ["禅城区", "南海区", "顺德区", "三水区", "高明区"], "江门市": ["蓬江区", "江海区", "新会区", "台山市", "开平市", "鹤山市", "恩平市"], "湛江市": ["赤坎区", "霞山区", "坡头区", "麻章区", "遂溪县", "徐闻县", "廉江市", "雷州市", "吴川市"], "茂名市": ["茂南区", "电白区", "高州市", "化州市", "信宜市"], "肇庆市": ["端州区", "鼎湖区", "高要区", "广宁县", "怀集县", "封开县", "德庆县", "四会市"], "惠州市": ["惠城区", "惠阳区", "博罗县", "惠东县", "龙门县"], "梅州市": ["梅江区", "梅县区", "大埔县", "丰顺县", "五华县", "平远县", "蕉岭县", "兴宁市"], "汕尾市": ["城区", "海丰县", "陆河县", "陆丰市"], "河源市": ["源城区", "紫金县", "龙川县", "连平县", "和平县", "东源县"], "阳江市": ["江城区", "阳东区", "阳西县", "阳春市"], "清远市": ["清城区", "清新区", "佛冈县", "阳山县", "连山壮族瑶族自治县", "连南瑶族自治县", "英德市", "连州市"], "东莞市": ["东莞市"], "中山市": ["中山市"], "潮州市": ["湘桥区", "潮安区", "饶平县"], "揭阳市": ["榕城区", "揭东区", "揭西县", "惠来县", "普宁市"], "云浮市": ["云城区", "云安区", "新兴县", "郁南县", "罗定市"] },
|
||||
"广西壮族自治区": { "南宁市": ["兴宁区", "青秀区", "江南区", "西乡塘区", "良庆区", "邕宁区", "武鸣区", "隆安县", "马山县", "上林县", "宾阳县", "横州市"], "柳州市": ["城中区", "鱼峰区", "柳南区", "柳北区", "柳江区", "柳城县", "鹿寨县", "融安县", "融水苗族自治县", "三江侗族自治县"], "桂林市": ["秀峰区", "叠彩区", "象山区", "七星区", "雁山区", "临桂区", "阳朔县", "灵川县", "全州县", "兴安县", "永福县", "灌阳县", "龙胜各族自治县", "资源县", "平乐县", "恭城瑶族自治县", "荔浦市"], "梧州市": ["万秀区", "长洲区", "龙圩区", "苍梧县", "藤县", "蒙山县", "岑溪市"], "北海市": ["海城区", "银海区", "铁山港区", "合浦县"], "防城港市": ["港口区", "防城区", "上思县", "东兴市"], "钦州市": ["钦南区", "钦北区", "灵山县", "浦北县"], "贵港市": ["港北区", "港南区", "覃塘区", "平南县", "桂平市"], "玉林市": ["玉州区", "福绵区", "容县", "陆川县", "博白县", "兴业县", "北流市"], "百色市": ["右江区", "田阳区", "田东县", "德保县", "那坡县", "凌云县", "乐业县", "田林县", "西林县", "隆林各族自治县", "靖西市", "平果市"], "贺州市": ["八步区", "平桂区", "昭平县", "钟山县", "富川瑶族自治县"], "河池市": ["金城江区", "宜州区", "南丹县", "天峨县", "凤山县", "东兰县", "罗城仫佬族自治县", "环江毛南族自治县", "巴马瑶族自治县", "都安瑶族自治县", "大化瑶族自治县", "罗城仫佬族自治县"], "来宾市": ["兴宾区", "忻城县", "象州县", "武宣县", "金秀瑶族自治县", "合山市"], "崇左市": ["江州区", "扶绥县", "宁明县", "龙州县", "大新县", "天等县", "凭祥市"] },
|
||||
"海南省": { "海口市": ["秀英区", "龙华区", "琼山区", "美兰区"], "三亚市": ["海棠区", "吉阳区", "天涯区", "崖州区"], "三沙市": ["西沙群岛", "南沙群岛", "中沙群岛"], "儋州市": ["那大镇", "和庆镇", "南丰镇", "大成镇", "雅星镇", "兰洋镇", "光村镇", "木棠镇", "海头镇", "峨蔓镇", "王五镇", "白马井镇", "中和镇", "排浦镇", "东成镇", "新州镇"] },
|
||||
"重庆市": { "市辖区": ["万州区", "涪陵区", "渝中区", "大渡口区", "江北区", "沙坪坝区", "九龙坡区", "南岸区", "北碚区", "渝北区", "巴南区", "黔江区", "长寿区", "江津区", "合川区", "永川区", "南川区", "綦江区", "大足区", "璧山区", "铜梁区", "潼南区", "荣昌区", "开州区", "梁平区", "武隆区"], "县": ["城口县", "丰都县", "垫江县", "忠县", "云阳县", "奉节县", "巫山县", "巫溪县", "石柱土家族自治县", "秀山土家族苗族自治县", "酉阳土家族苗族自治县", "彭水苗族土家族自治县"] },
|
||||
"四川省": { "成都市": ["锦江区", "青羊区", "金牛区", "武侯区", "成华区", "龙泉驿区", "青白江区", "新都区", "温江区", "双流区", "郫都区", "新津区", "金堂县", "大邑县", "蒲江县", "都江堰市", "彭州市", "邛崃市", "崇州市", "简阳市", "东部新区"], "自贡市": ["自流井区", "贡井区", "大安区", "沿滩区", "荣县", "富顺县"], "攀枝花市": ["东区", "西区", "仁和区", "米易县", "盐边县"], "泸州市": ["江阳区", "纳溪区", "龙马潭区", "泸县", "合江县", "叙永县", "古蔺县"], "德阳市": ["旌阳区", "罗江区", "中江县", "广汉市", "什邡市", "绵竹市"], "绵阳市": ["涪城区", "游仙区", "安州区", "三台县", "盐亭县", "梓潼县", "北川羌族自治县", "平武县", "江油市"], "广元市": ["利州区", "昭化区", "朝天区", "旺苍县", "青川县", "剑阁县", "苍溪县"], "遂宁市": ["船山区", "安居区", "蓬溪县", "大英县", "射洪市"], "内江市": ["市中区", "东兴区", "威远县", "资中县", "隆昌市"], "乐山市": ["市中区", "沙湾区", "五通桥区", "金口河区", "犍为县", "井研县", "夹江县", "沐川县", "峨边彝族自治县", "马边彝族自治县", "峨眉山市"], "南充市": ["顺庆区", "高坪区", "嘉陵区", "南部县", "营山县", "蓬安县", "仪陇县", "西充县", "阆中市"], "眉山市": ["东坡区", "彭山区", "仁寿县", "洪雅县", "丹棱县", "青神县"], "宜宾市": ["翠屏区", "南溪区", "叙州区", "江安县", "长宁县", "高县", "珙县", "筠连县", "兴文县", "屏山县"], "广安市": ["广安区", "前锋区", "岳池县", "武胜县", "邻水县", "华蓥市"], "达州市": ["通川区", "达川区", "宣汉县", "开江县", "大竹县", "渠县", "万源市"], "雅安市": ["雨城区", "名山区", "荥经县", "汉源县", "石棉县", "天全县", "芦山县", "宝兴县"], "巴中市": ["巴州区", "恩阳区", "通江县", "南江县", "平昌县"], "资阳市": ["雁江区", "安岳县", "乐至县"], "阿坝藏族羌族自治州": ["马尔康市", "汶川县", "理县", "茂县", "松潘县", "九寨沟县", "黄龙县", "小金县", "黑水县", "壤塘县", "阿坝县", "若尔盖县", "红原县"], "甘孜藏族自治州": ["康定市", "泸定县", "丹巴县", "九龙县", "雅江县", "道孚县", "理塘县", "德格县", "白玉县", "石渠县", "新龙县", "色达县", "理塘县", "巴塘县", "乡城县", "稻城县", "得荣县"], "凉山彝族自治州": ["西昌市", "木里藏族自治县", "盐源县", "德昌县", "会理县", "会东县", "宁南县", "普格县", "布拖县", "金阳县", "昭觉县", "喜德县", "冕宁县", "越西县", "甘洛县", "美姑县", "雷波县"] },
|
||||
"贵州省": { "贵阳市": ["南明区", "云岩区", "花溪区", "乌当区", "白云区", "观山湖区", "开阳县", "息烽县", "修文县", "清镇市"], "六盘水市": ["钟山区", "水城区", "盘州市", "六枝特区"], "遵义市": ["红花岗区", "汇川区", "播州区", "桐梓县", "绥阳县", "正安县", "道真仡佬族苗族自治县", "务川仡佬族苗族自治县", "凤冈县", "湄潭县", "余庆县", "习水县", "赤水市", "仁怀市"], "安顺市": ["西秀区", "平坝区", "普定县", "镇宁布依族苗族自治县", "关岭布依族苗族自治县", "紫云苗族布依族自治县"], "毕节市": ["七星关区", "大方县", "黔西县", "金沙县", "织金县", "纳雍县", "威宁彝族回族苗族自治县", "赫章县", "百里杜鹃管理区"], "铜仁市": ["碧江区", "万山区", "江口县", "玉屏侗族自治县", "石阡县", "思南县", "印江土家族苗族自治县", "德江县", "沿河土家族自治县", "松桃苗族自治县"], "黔西南布依族苗族自治州": ["兴义市", "兴仁市", "普安县", "晴隆县", "贞丰县", "望谟县", "册亨县", "安龙县"], "黔东南苗族侗族自治州": ["凯里市", "黄平县", "施秉县", "三穗县", "镇远县", "岑巩县", "天柱县", "锦屏县", "剑河县", "台江县", "黎平县", "榕江县", "从江县", "雷山县", "麻江县", "丹寨县"], "黔南布依族苗族自治州": ["都匀市", "福泉市", "荔波县", "贵定县", "瓮安县", "独山县", "平塘县", "罗甸县", "长顺县", "龙里县", "惠水县", "三都水族自治县"] },
|
||||
"云南省": { "昆明市": ["五华区", "盘龙区", "官渡区", "西山区", "东川区", "呈贡区", "晋宁区", "富民县", "宜良县", "石林彝族自治县", "嵩明县", "禄劝彝族苗族自治县", "寻甸回族彝族自治县", "安宁市"], "曲靖市": ["麒麟区", "沾益区", "马龙区", "陆良县", "师宗县", "罗平县", "富源县", "会泽县", "宣威市"], "玉溪市": ["红塔区", "江川区", "澄江县", "通海县", "华宁县", "易门县", "峨山彝族自治县", "新平彝族傣族自治县", "元江哈尼族彝族傣族自治县"], "保山市": ["隆阳区", "施甸县", "龙陵县", "昌宁县", "腾冲市"], "昭通市": ["昭阳区", "鲁甸县", "巧家县", "盐津县", "大关县", "永善县", "绥江县", "镇雄县", "彝良县", "威信县", "水富市"], "丽江市": ["古城区", "玉龙纳西族自治县", "永胜县", "华坪县", "宁蒗彝族自治县"], "普洱市": ["思茅区", "宁洱哈尼族彝族自治县", "墨江哈尼族自治县", "景东彝族自治县", "景谷傣族彝族自治县", "镇沅彝族哈尼族拉祜族自治县", "江城哈尼族彝族自治县", "孟连傣族拉祜族佤族自治县", "澜沧拉祜族自治县", "西盟佤族自治县"], "临沧市": ["临翔区", "凤庆县", "云县", "永德县", "镇康县", "双江拉祜族佤族傣族自治县", "耿马傣族佤族自治县", "沧源佤族自治县"], "楚雄彝族自治州": ["楚雄市", "双柏县", "牟定县", "南华县", "姚安县", "大姚县", "永仁县", "元谋县", "武定县", "禄丰县"], "红河哈尼族彝族自治州": ["个旧市", "开远市", "蒙自市", "弥勒市", "屏边苗族自治县", "建水县", "石屏县", "泸西县", "元阳县", "红河县", "绿春县", "河口瑶族自治县"], "文山壮族苗族自治州": ["文山市", "砚山县", "西畴县", "麻栗坡县", "马关县", "丘北县", "广南县", "富宁县"], "西双版纳傣族自治州": ["景洪市", "勐海县", "勐腊县"], "大理白族自治州": ["大理市", "漾濞彝族自治县", "祥云县", "宾川县", "弥渡县", "南涧彝族自治县", "巍山彝族回族自治县", "永平县", "云龙县", "洱源县", "剑川县", "鹤庆县"], "德宏傣族景颇族自治州": ["芒市", "瑞丽市", "梁河县", "盈江县", "陇川县"], "怒江傈僳族自治州": ["泸水市", "福贡县", "贡山独龙族怒族自治县", "兰坪白族普米族自治县"], "迪庆藏族自治州": ["香格里拉市", "德钦县", "维西傈僳族自治县"] },
|
||||
"西藏自治区": { "拉萨市": ["城关区", "堆龙德庆区", "达孜区", "林周县", "当雄县", "尼木县", "曲水县", "墨竹工卡县"], "日喀则市": ["桑珠孜区", "南木林县", "江孜县", "定日县", "萨迦县", "拉孜县", "仲巴县", "吉隆县", "聂拉木县", "萨嘎县", "岗巴县"], "昌都市": ["卡若区", "江达县", "贡觉县", "类乌齐县", "丁青县", "察雅县", "八宿县", "左贡县", "芒康县", "洛隆县", "边坝县"], "林芝市": ["巴宜区", "工布江达县", "米林县", "墨脱县", "波密县", "察隅县", "朗县"], "山南市": ["乃东区", "扎囊县", "贡嘎县", "桑日县", "琼结县", "曲松县", "措美县", "洛扎县", "加查县", "隆子县", "错那县", "浪卡子县"], "那曲市": ["色尼区", "嘉黎县", "比如县", "聂荣县", "安多县", "申扎县", "索县", "班戈县", "巴青县", "尼玛县", "双湖县"], "阿里地区": ["噶尔县", "普兰县", "札达县", "日土县", "革吉县", "改则县", "措勤县"] },
|
||||
"陕西省": { "西安市": ["新城区", "碑林区", "莲湖区", "灞桥区", "未央区", "雁塔区", "阎良区", "临潼区", "长安区", "高陵区", "鄠邑区", "蓝田县", "周至县"], "铜川市": ["王益区", "印台区", "耀州区", "宜君县"], "宝鸡市": ["渭滨区", "金台区", "陈仓区", "凤翔区", "岐山县", "扶风县", "眉县", "千阳县", "陇县", "太白县", "凤县"], "咸阳市": ["秦都区", "杨陵区", "渭城区", "三原县", "泾阳县", "乾县", "礼泉县", "永寿县", "长武县", "旬邑县", "淳化县", "武功县", "兴平市", "彬州市"], "渭南市": ["临渭区", "华州区", "潼关县", "大荔县", "合阳县", "澄城县", "蒲城县", "白水县", "富平县", "韩城市", "华阴市"], "延安市": ["宝塔区", "安塞区", "延长县", "延川县", "子长市", "志丹县", "吴起县", "甘泉县", "富县", "洛川县", "宜川县", "黄龙县", "黄陵县"], "汉中市": ["汉台区", "南郑区", "城固县", "洋县", "西乡县", "勉县", "宁强县", "略阳县", "镇巴县", "留坝县", "佛坪县"], "榆林市": ["榆阳区", "横山区", "府谷县", "靖边县", "定边县", "绥德县", "米脂县", "佳县", "吴堡县", "清涧县", "子洲县", "神木市"], "安康市": ["汉滨区", "汉阴县", "石泉县", "宁陕县", "紫阳县", "岚皋县", "平利县", "镇坪县", "旬阳县", "白河县"], "商洛市": ["商州区", "洛南县", "丹凤县", "商南县", "山阳县", "镇安县", "柞水县"] },
|
||||
"甘肃省": { "兰州市": ["城关区", "七里河区", "西固区", "安宁区", "红古区", "永登县", "皋兰县", "榆中县"], "嘉峪关市": ["嘉峪关市"], "金昌市": ["金川区", "永昌县"], "白银市": ["白银区", "平川区", "靖远县", "会宁县", "景泰县"], "天水市": ["秦州区", "麦积区", "清水县", "秦安县", "甘谷县", "武山县", "张家川回族自治县"], "武威市": ["凉州区", "民勤县", "古浪县", "天祝藏族自治县"], "张掖市": ["甘州区", "肃南裕固族自治县", "民乐县", "临泽县", "高台县", "山丹县"], "平凉市": ["崆峒区", "泾川县", "灵台县", "崇信县", "庄浪县", "静宁县", "华亭市"], "酒泉市": ["肃州区", "金塔县", "瓜州县", "肃北蒙古族自治县", "阿克塞哈萨克族自治县", "玉门市", "敦煌市"], "庆阳市": ["西峰区", "庆城县", "环县", "华池县", "合水县", "正宁县", "宁县", "镇原县"], "定西市": ["安定区", "通渭县", "陇西县", "渭源县", "临洮县", "漳县", "岷县"], "陇南市": ["武都区", "成县", "文县", "宕昌县", "康县", "西和县", "礼县", "徽县", "两当县"], "临夏回族自治州": ["临夏市", "临夏县", "康乐县", "永靖县", "广河县", "和政县", "东乡族自治县", "积石山保安族东乡族撒拉族自治县"], "甘南藏族自治州": ["合作市", "临潭县", "卓尼县", "舟曲县", "迭部县", "玛曲县", "碌曲县", "夏河县"] },
|
||||
"青海省": { "西宁市": ["城东区", "城中区", "城西区", "城北区", "湟中区", "大通回族土族自治县", "湟源县"], "海东市": ["乐都区", "平安区", "民和回族土族自治县", "互助土族自治县", "化隆回族自治县", "循化撒拉族自治县"], "海北藏族自治州": ["海晏县", "祁连县", "刚察县", "门源回族自治县"], "黄南藏族自治州": ["同仁市", "尖扎县", "泽库县", "河南蒙古族自治县"], "海南藏族自治州": ["共和县", "同德县", "贵德县", "兴海县", "贵南县"], "果洛藏族自治州": ["玛沁县", "班玛县", "甘德县", "达日县", "久治县", "玛曲县"], "玉树藏族自治州": ["玉树市", "杂多县", "称多县", "治多县", "囊谦县", "曲麻莱县"], "海西蒙古族藏族自治州": ["格尔木市", "德令哈市", "茫崖市", "乌兰县", "都兰县", "天峻县", "大柴旦行政区"] },
|
||||
"宁夏回族自治区": { "银川市": ["兴庆区", "西夏区", "金凤区", "永宁县", "贺兰县", "灵武市"], "石嘴山市": ["大武口区", "惠农区", "平罗县"], "吴忠市": ["利通区", "红寺堡区", "盐池县", "同心县", "青铜峡市"], "固原市": ["原州区", "西吉县", "隆德县", "泾源县", "彭阳县"], "中卫市": ["沙坡头区", "中宁县", "海原县"] },
|
||||
"新疆维吾尔自治区": { "乌鲁木齐市": ["天山区", "沙依巴克区", "新市区", "水磨沟区", "头屯河区", "达坂城区", "米东区", "乌鲁木齐县"], "克拉玛依市": ["克拉玛依区", "独山子区", "白碱滩区", "乌尔禾区"], "吐鲁番市": ["高昌区", "鄯善县", "托克逊县"], "哈密市": ["伊州区", "伊吾县", "巴里坤哈萨克自治县"], "昌吉回族自治州": ["昌吉市", "阜康市", "呼图壁县", "玛纳斯县", "奇台县", "吉木萨尔县", "木垒哈萨克自治县"], "博尔塔拉蒙古自治州": ["博乐市", "阿拉山口市", "精河县", "温泉县"], "巴音郭楞蒙古自治州": ["库尔勒市", "轮台县", "尉犁县", "若羌县", "且末县", "焉耆回族自治县", "和静县", "和硕县", "博湖县"], "阿克苏地区": ["阿克苏市", "温宿县", "库车市", "沙雅县", "新和县", "拜城县", "乌什县", "阿瓦提县", "柯坪县"], "克孜勒苏柯尔克孜自治州": ["阿图什市", "阿克陶县", "阿合奇县", "乌恰县"], "喀什地区": ["喀什市", "疏附县", "疏勒县", "英吉沙县", "泽普县", "莎车县", "叶城县", "麦盖提县", "岳普湖县", "伽师县", "巴楚县", "塔什库尔干塔吉克自治县"], "和田地区": ["和田市", "和田县", "墨玉县", "皮山县", "洛浦县", "策勒县", "于田县", "民丰县"], "伊犁哈萨克自治州": ["伊宁市", "奎屯市", "霍尔果斯市", "伊宁县", "察布查尔锡伯自治县", "霍城县", "巩留县", "新源县", "昭苏县", "特克斯县", "尼勒克县"], "塔城地区": ["塔城市", "乌苏市", "沙湾市", "额敏县", "托里县", "裕民县", "和布克赛尔蒙古自治县"], "阿勒泰地区": ["阿勒泰市", "布尔津县", "富蕴县", "福海县", "哈巴河县", "青河县", "吉木乃县"], "石河子市": ["石河子市"], "阿拉尔市": ["阿拉尔市"], "图木舒克市": ["图木舒克市"], "五家渠市": ["五家渠市"], "北屯市": ["北屯市"], "铁门关市": ["铁门关市"], "双河市": ["双河市"], "可克达拉市": ["可克达拉市"], "昆玉市": ["昆玉市"], "胡杨河市": ["胡杨河市"] },
|
||||
"台湾省": { "台湾省": ["台北市", "新北市", "桃园市", "台中市", "台南市", "高雄市", "基隆市", "新竹市", "嘉义市", "新竹县", "苗栗县", "彰化县", "南投县", "云林县", "嘉义县", "屏东县", "宜兰县", "花莲县", "台东县", "澎湖县", "金门县", "连江县"] },
|
||||
"香港特别行政区": { "香港": ["中西区", "东区", "南区", "湾仔区", "九龙城区", "观塘区", "深水埗区", "油尖旺区", "黄大仙区", "离岛区", "北区", "西贡区", "沙田区", "大埔区", "荃湾区", "屯门区", "元朗区"] },
|
||||
"澳门特别行政区": { "澳门": ["花地玛堂区", "圣安多尼堂区", "大堂区", "望德堂区", "风顺堂区", "嘉模堂区", "圣方济各堂区"] }
|
||||
};
|
||||
|
||||
// 初始化省份下拉框
|
||||
function initProvinces() {
|
||||
const provinceSelect = document.getElementById('province');
|
||||
for (const province in addressData) {
|
||||
const option = document.createElement('option');
|
||||
option.value = province;
|
||||
option.textContent = province;
|
||||
provinceSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
function loadCities() {
|
||||
const province = document.getElementById('province').value;
|
||||
const citySelect = document.getElementById('city');
|
||||
const districtSelect = document.getElementById('district');
|
||||
|
||||
citySelect.innerHTML = '<option value="">请选择城市</option>';
|
||||
districtSelect.innerHTML = '<option value="">请选择区县</option>';
|
||||
|
||||
if (!province) return;
|
||||
|
||||
const cities = addressData[province];
|
||||
for (const city in cities) {
|
||||
const option = document.createElement('option');
|
||||
option.value = city;
|
||||
option.textContent = city;
|
||||
citySelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
function loadDistricts() {
|
||||
const province = document.getElementById('province').value;
|
||||
const city = document.getElementById('city').value;
|
||||
const districtSelect = document.getElementById('district');
|
||||
|
||||
districtSelect.innerHTML = '<option value="">请选择区县</option>';
|
||||
|
||||
if (!province || !city) return;
|
||||
|
||||
const districts = addressData[province][city];
|
||||
if (districts) {
|
||||
for (const district of districts) {
|
||||
const option = document.createElement('option');
|
||||
option.value = district;
|
||||
option.textContent = district;
|
||||
districtSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getFullAddress() {
|
||||
const province = document.getElementById('province').value;
|
||||
const city = document.getElementById('city').value;
|
||||
const district = document.getElementById('district').value;
|
||||
const detail = document.getElementById('customerAddress').value;
|
||||
const parts = [province, city, district, detail].filter(p => p);
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
function setAddressFields(fullAddress) {
|
||||
if (!fullAddress) {
|
||||
document.getElementById('province').value = '';
|
||||
document.getElementById('city').innerHTML = '<option value="">请选择城市</option>';
|
||||
document.getElementById('district').innerHTML = '<option value="">请选择区县</option>';
|
||||
document.getElementById('customerAddress').value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 尝试解析地址并回填
|
||||
let matched = false;
|
||||
for (const province in addressData) {
|
||||
if (fullAddress.startsWith(province)) {
|
||||
document.getElementById('province').value = province;
|
||||
// 加载城市
|
||||
const cities = addressData[province];
|
||||
const citySelect = document.getElementById('city');
|
||||
citySelect.innerHTML = '<option value="">请选择城市</option>';
|
||||
for (const city in cities) {
|
||||
const option = document.createElement('option');
|
||||
option.value = city;
|
||||
option.textContent = city;
|
||||
citySelect.appendChild(option);
|
||||
}
|
||||
|
||||
// 提取城市部分
|
||||
const remaining = fullAddress.substring(province.length);
|
||||
for (const city in cities) {
|
||||
if (remaining.startsWith(city)) {
|
||||
citySelect.value = city;
|
||||
// 加载区县
|
||||
const districts = cities[city];
|
||||
const districtSelect = document.getElementById('district');
|
||||
districtSelect.innerHTML = '<option value="">请选择区县</option>';
|
||||
for (const district of districts) {
|
||||
const option = document.createElement('option');
|
||||
option.value = district;
|
||||
option.textContent = district;
|
||||
districtSelect.appendChild(option);
|
||||
}
|
||||
|
||||
// 提取区县和详细地址
|
||||
const afterCity = remaining.substring(city.length);
|
||||
for (const district of districts) {
|
||||
if (afterCity.startsWith(district)) {
|
||||
districtSelect.value = district;
|
||||
// 剩余的是详细地址
|
||||
document.getElementById('customerAddress').value = afterCity.substring(district.length);
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
// 没匹配到区县,详细地址就是剩余部分
|
||||
document.getElementById('customerAddress').value = afterCity;
|
||||
matched = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
// 没匹配到城市,详细地址就是剩余部分
|
||||
document.getElementById('customerAddress').value = remaining;
|
||||
}
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
// 没有匹配到任何省,直接放入详细地址
|
||||
document.getElementById('customerAddress').value = fullAddress;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
initProvinces();
|
||||
loadData();
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
管理后台 → 客户管理列表-操作点击"生成证书"
|
||||
↓
|
||||
跳转到 generateLicense.html?customerId=xxx
|
||||
↓
|
||||
自动填入客户名称
|
||||
↓
|
||||
填入硬件信息 → 生成证书
|
||||
↓
|
||||
自动保存 License 记录到数据库
|
||||
↓
|
||||
弹出下载框
|
||||
↓
|
||||
回管理后台能看到新建的 License 记录
|
||||
|
||||
重启服务后试试看。
|
||||
|
||||
自动保存 License 记录到数据库 ,能不能把生成的两个文件存储到数据库中呀
|
||||
|
||||
打包下载
|
||||
|
||||
- 无 License:生成证书 + 编辑 + 删除
|
||||
- 有 License:续期 + 冻结/解冻 + 编辑 + 删除 + 生成证书
|
||||
Loading…
Reference in New Issue