├── etc └── china.png ├── .gitignore ├── .editorconfig ├── tests ├── TestCase.php ├── ChinaTest.php ├── Holiday │ ├── HolidayServiceTest.php │ ├── DateTest.php │ ├── HolidayLoaderTest.php │ └── HolidayTest.php ├── Nationality │ ├── NationalityTest.php │ ├── NationalityServiceTest.php │ └── NationalityLoaderTest.php ├── Region │ ├── AddressFactoryTest.php │ ├── Location │ │ └── AddressTest.php │ ├── RegionCollectionTest.php │ └── RegionServiceTest.php └── IDCard │ └── IDCardTest.php ├── bin └── china ├── .travis.yml ├── src ├── Common │ ├── ResourceFile.php │ ├── Utils.php │ ├── FilesystemAwareInterface.php │ ├── ResourceLoader │ │ ├── ResourceLoaderInterface.php │ │ └── LazyLoader.php │ └── FilesystemAwareTrait.php ├── Region │ ├── RegionCollection.php │ ├── Location │ │ ├── City.php │ │ ├── District.php │ │ ├── Province.php │ │ ├── AddressInterface.php │ │ └── Address.php │ ├── RegionCollectionInterface.php │ ├── RegionLoader.php │ ├── AddressFinderTrait.php │ ├── RegionServiceInterface.php │ └── RegionService.php ├── Nationality │ ├── NationalityLoader.php │ ├── NationalityInterface.php │ ├── NationalityServiceInterface.php │ ├── NationalityService.php │ └── Nationality.php ├── Holiday │ ├── HolidayLoader.php │ ├── DateInterface.php │ ├── HolidayInterface.php │ ├── Holiday.php │ ├── HolidayServiceInterface.php │ ├── HolidayService.php │ └── Date.php ├── IDCard │ ├── IDCardInterface.php │ ├── IDCard.php │ └── IDCardUtils.php ├── Command │ ├── DashboardCommand.php │ ├── CrawlCommand.php │ ├── ShowNationalityCommand.php │ ├── Application.php │ ├── ShowRegionCommand.php │ ├── CrawlHolidayCommand.php │ ├── ShowHolidayCommand.php │ ├── CrawlNationalityCommand.php │ └── CrawlRegionCommand.php └── China.php ├── doc ├── 4-nationality.md ├── 3-holiday.md ├── 0-index.md ├── 1-basic.md └── 2-region.md ├── phpunit.xml.dist ├── composer.json ├── LICENSE ├── .github └── workflows │ └── test.yml ├── .scrutinizer.yml ├── resources ├── regions │ ├── provinces.json │ └── cities.json ├── nationalities.json └── holidays.json └── README.md /etc/china.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slince/China/HEAD/etc/china.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/* 2 | .DS_Store 3 | .idea/* 4 | composer.lock 5 | .php_cs.cache -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; top-most EditorConfig file 2 | root = true 3 | 4 | ; Unix-style newlines 5 | [*] 6 | end_of_line = LF 7 | 8 | [*.php] 9 | indent_style = space 10 | indent_size = 4 -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | run(); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # see http://about.travis-ci.org/docs/user/languages/php/ for more hints 2 | language: php 3 | 4 | php: 5 | - 5.6 6 | - 7.0 7 | - 7.1 8 | 9 | before_script: 10 | - composer install 11 | 12 | script: ./vendor/bin/phpunit --coverage-clover=coverage.xml 13 | 14 | after_success: 15 | - bash <(curl -s https://codecov.io/bash) 16 | 17 | notifications: 18 | email: false -------------------------------------------------------------------------------- /src/Common/ResourceFile.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Common; 15 | 16 | class ResourceFile extends \SplFileInfo 17 | { 18 | } -------------------------------------------------------------------------------- /doc/4-nationality.md: -------------------------------------------------------------------------------- 1 | ## 民族查询 2 | 3 | 我国一共有56个民族,目前此服务只提供了基本的数据查询服务;如名称,人口以及拼音写法;此项服务数据摘自百度百科; 4 | 5 | ### 查找所有的民族 6 | ```php 7 | 8 | $nationalityService = $chna->getNationality(); 9 | 10 | print_r($nationalityService->findAll()->count()); //56 11 | 12 | ``` 13 | 14 | ### 按名称查找 15 | 16 | ```php 17 | $hezhe = $nationalityService->find('赫哲族'): 18 | 19 | echo $hezhe->getPinyin(); //输出“hè zhé zú” 20 | echo $hezhe->getPopulation(); //输出人口 21 | ``` -------------------------------------------------------------------------------- /tests/ChinaTest.php: -------------------------------------------------------------------------------- 1 | getChina(); 10 | $this->assertEquals('中国', $china->getName()); 11 | $this->assertEquals('中华人民共和国', $china->getOfficialName()); 12 | $this->assertEquals('CN', $china->getIsoCode()); 13 | $this->assertEquals(['zh_CN', 'zh_TW'], $china->getLanguages()); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Common/Utils.php: -------------------------------------------------------------------------------- 1 | getChina()->getHoliday(); 12 | $holidays = $holidayService->findAll(); 13 | $this->assertGreaterThan(0, count($holidays)); 14 | $this->assertNotEmpty($holidays->toArray()); 15 | // var_dump($holidays->toArray()); 16 | } 17 | } -------------------------------------------------------------------------------- /tests/Holiday/DateTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(12, $date->getMonth()); 15 | $this->assertEquals(8, $date->getDay()); 16 | $this->assertEquals('12月8日', $date->toString()); 17 | $this->assertEquals('12-8', $date->format('{month}-{day}')); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Region/RegionCollection.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region; 15 | 16 | use Doctrine\Common\Collections\ArrayCollection; 17 | 18 | class RegionCollection extends ArrayCollection implements RegionCollectionInterface 19 | { 20 | use AddressFinderTrait; 21 | } -------------------------------------------------------------------------------- /tests/Nationality/NationalityTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('阿昌族', $nationality->getName()); 14 | $this->assertEquals('ā chāng zú', $nationality->getPinyin()); 15 | $this->assertEquals(39555, $nationality->getPopulation()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Region/Location/City.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region\Location; 15 | 16 | class City extends Address 17 | { 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | public static function getType(): string 22 | { 23 | return AddressInterface::TYPE_CITY; 24 | } 25 | } -------------------------------------------------------------------------------- /doc/3-holiday.md: -------------------------------------------------------------------------------- 1 | ## 节日查询 2 | 3 | ### 获取所有的节日 4 | 5 | ```php 6 | 7 | $holidayService = $china->getHoliday(); 8 | 9 | print_r($holidayService->findAll()); 10 | 11 | ``` 12 | ### 按名称查找节日 13 | 14 | ```php 15 | $yuandan = $holidayService->find('元旦节'): 16 | 17 | echo $yuandan->getDate(); //输出 1月1日 18 | ``` 19 | 20 | ### 按照节日类型 21 | 22 | 1. 获取传统节日 23 | ```php 24 | $holidayService->findTraditionalHolidays(); 25 | ``` 26 | 27 | 2. 获取国际通用性节日 28 | ```php 29 | $holidayService->findInternationalHolidays(); 30 | ``` 31 | 32 | 3. 获取24节气 33 | 34 | ```php 35 | $holidayService->findSolarTermHolidays(); 36 | ``` -------------------------------------------------------------------------------- /src/Region/Location/District.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region\Location; 15 | 16 | class District extends Address 17 | { 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | public static function getType(): string 22 | { 23 | return AddressInterface::TYPE_DISTRICT; 24 | } 25 | } -------------------------------------------------------------------------------- /src/Region/Location/Province.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region\Location; 15 | 16 | class Province extends Address 17 | { 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | public static function getType(): string 22 | { 23 | return AddressInterface::TYPE_PROVINCE; 24 | } 25 | } -------------------------------------------------------------------------------- /tests/Holiday/HolidayLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertNotEmpty($holidays->toArray()); 17 | $this->assertInstanceOf(HolidayInterface::class, $holidays->first()); 18 | } 19 | } -------------------------------------------------------------------------------- /doc/0-index.md: -------------------------------------------------------------------------------- 1 | ## 目录 2 | 3 | 1. [基本用法](1-basic.md) 4 | 2. [地区查询](2-region.md) 5 | 3. [节日查询](3-holiday.md) 6 | 4. [民族查询](4-nationality.md) 7 | 8 | ## 命令行工具 9 | 10 | 执行下面命令查看支持的所有命令 11 | 12 | ```bash 13 | $ china list 14 | ``` 15 | 输出信息: 16 | ``` 17 | Available commands: 18 | help Displays help for a command 19 | list Lists commands 20 | crawl 21 | crawl:holiday 采集节假日数据 22 | crawl:nationality 从百度百科采集民族信息 23 | crawl:region 从国家统计局采集地区信息 24 | view 25 | view:holiday 展示节假日信息 26 | view:nationality 展示民族数据信息 27 | view:region 展示我国省市县信息 28 | ``` 29 | 30 | ## TODO 31 | ... -------------------------------------------------------------------------------- /tests/Nationality/NationalityServiceTest.php: -------------------------------------------------------------------------------- 1 | getChina()->getNationality(); 12 | $han = $nationalityService->find('汉族'); 13 | $this->assertGreaterThan(0, $han->getPopulation()); 14 | } 15 | 16 | public function testFindAll() 17 | { 18 | $nationalityService = $this->getChina()->getNationality(); 19 | $this->assertGreaterThan(0, count($nationalityService->findAll())); 20 | } 21 | } -------------------------------------------------------------------------------- /tests/Holiday/HolidayTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('元旦节', $holiday->getName()); 16 | $this->assertEquals(HolidayInterface::TYPE_INTERNATIONAL, $holiday->getType()); 17 | $this->assertEquals('1月1日', $holiday->getDate()->toString()); 18 | } 19 | } -------------------------------------------------------------------------------- /tests/Nationality/NationalityLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertNotEmpty($holidays->toArray()); 17 | $this->assertInstanceOf(NationalityInterface::class, $holidays->first()); 18 | } 19 | } -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./src 6 | 7 | 8 | ./src/Command 9 | ./src/Common/FilesystemAwareTrait.php 10 | 11 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Nationality/NationalityLoader.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Nationality; 15 | 16 | use China\Common\ResourceLoader\LazyLoader; 17 | 18 | class NationalityLoader extends LazyLoader 19 | { 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function createRecord($record) 24 | { 25 | return new Nationality($record['name'], $record['pinyin'], intval($record['population'])); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Holiday/HolidayLoader.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | use China\Common\ResourceLoader\LazyLoader; 17 | 18 | class HolidayLoader extends LazyLoader 19 | { 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | public function createRecord(array $record) 24 | { 25 | $date = explode('月', trim($record['date'], '日')); 26 | return new Holiday($record['name'], $record['type'], new Date(intval($date[0]), intval($date[1]))); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Common/FilesystemAwareInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Common; 15 | 16 | use Symfony\Component\Filesystem\Filesystem; 17 | 18 | interface FilesystemAwareInterface 19 | { 20 | /** 21 | * 获取文件系统 22 | * 23 | * @return Filesystem 24 | */ 25 | public function getFilesystem(): Filesystem; 26 | 27 | /** 28 | * 设置文件系统 29 | * 30 | * @param Filesystem $filesystem 31 | */ 32 | public function setFilesystem(Filesystem $filesystem); 33 | } -------------------------------------------------------------------------------- /src/IDCard/IDCardInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\IDCard; 15 | 16 | interface IDCardInterface 17 | { 18 | /** 19 | * 转换成字符串. 20 | * 21 | * @return string 22 | */ 23 | public function __toString(); 24 | 25 | /** 26 | * 是否是短位身份证 27 | * 28 | * @return boolean 29 | */ 30 | public function isShortLength(): bool; 31 | 32 | /** 33 | * 获取长数字. 34 | * 35 | * @return string 36 | */ 37 | public function getLongNumber(): string; 38 | } -------------------------------------------------------------------------------- /src/Nationality/NationalityInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Nationality; 15 | 16 | interface NationalityInterface extends \JsonSerializable 17 | { 18 | /** 19 | * 民族名称 20 | * @return string 21 | */ 22 | public function getName(): string; 23 | 24 | /** 25 | * 民族拼音 26 | * @return string 27 | */ 28 | public function getPinyin(): string; 29 | 30 | /** 31 | * 人口数量 32 | * @return int 33 | */ 34 | public function getPopulation(): int; 35 | } -------------------------------------------------------------------------------- /src/Nationality/NationalityServiceInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Nationality; 15 | 16 | use Doctrine\Common\Collections\Collection; 17 | 18 | interface NationalityServiceInterface 19 | { 20 | /** 21 | * 获取所有的节假日. 22 | * 23 | * @return Collection 24 | */ 25 | public function findAll(); 26 | 27 | /** 28 | * 查找指定的民族. 29 | * 30 | * @param string $name 31 | * 32 | * @return NationalityInterface 33 | */ 34 | public function find($name); 35 | } -------------------------------------------------------------------------------- /src/Command/DashboardCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\China; 17 | use Symfony\Component\Console\Command\Command; 18 | 19 | class DashboardCommand extends Command 20 | { 21 | /** 22 | * @var China 23 | */ 24 | protected $china; 25 | 26 | /** 27 | * 获取china对象 28 | * 29 | * @return China 30 | */ 31 | protected function getChina(): China 32 | { 33 | if (null === $this->china) { 34 | $this->china = new China(); 35 | } 36 | return $this->china; 37 | } 38 | } -------------------------------------------------------------------------------- /src/Common/ResourceLoader/ResourceLoaderInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Common\ResourceLoader; 15 | 16 | use China\Common\ResourceFile; 17 | use Doctrine\Common\Collections\Collection; 18 | 19 | interface ResourceLoaderInterface extends Collection 20 | { 21 | /** 22 | * 获取资源文件. 23 | * 24 | * @return ResourceFile 25 | */ 26 | public function getResourceFile(): ResourceFile; 27 | 28 | /** 29 | * 处理原生数据. 30 | * 31 | * @param array $record 32 | * @return array|object 33 | */ 34 | public function createRecord(array $record); 35 | } -------------------------------------------------------------------------------- /src/Region/RegionCollectionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region; 15 | 16 | use China\Region\Location\AddressInterface; 17 | use Doctrine\Common\Collections\Collection; 18 | 19 | interface RegionCollectionInterface extends Collection 20 | { 21 | /** 22 | * 根据Code查找地址 23 | * 24 | * @param string $code 25 | * 26 | * @return AddressInterface|null 27 | */ 28 | public function findByCode(string $code): ?AddressInterface; 29 | 30 | /** 31 | * 根据Name查找地址 32 | * 33 | * @param string $name 34 | * 35 | * @return AddressInterface|null 36 | */ 37 | public function findByName(string $name): ?AddressInterface; 38 | } -------------------------------------------------------------------------------- /src/Common/FilesystemAwareTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Common; 15 | 16 | use Symfony\Component\Filesystem\Filesystem; 17 | 18 | trait FilesystemAwareTrait 19 | { 20 | /** 21 | * @var Filesystem 22 | */ 23 | protected $filesystem; 24 | 25 | /** 26 | * 设置文件系统 27 | * 28 | * @param Filesystem $filesystem 29 | */ 30 | public function setFilesystem(Filesystem $filesystem) 31 | { 32 | $this->filesystem = $filesystem; 33 | } 34 | 35 | /** 36 | * 获取文件系统 37 | * 38 | * @return Filesystem 39 | */ 40 | public function getFilesystem(): Filesystem 41 | { 42 | return $this->filesystem; 43 | } 44 | } -------------------------------------------------------------------------------- /src/Holiday/DateInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | interface DateInterface extends \JsonSerializable 17 | { 18 | /** 19 | * 获取天. 20 | * 21 | * @return int 22 | */ 23 | public function getDay(): int; 24 | 25 | /** 26 | * 获取month. 27 | * 28 | * @return int 29 | */ 30 | public function getMonth(): int; 31 | 32 | /** 33 | * 转换成字符串表达式. 34 | * 35 | * @return string 36 | */ 37 | public function toString(): string; 38 | 39 | /** 40 | * 格式化输出日期 41 | * 42 | * @param string $format 43 | * 44 | * @return string 45 | */ 46 | public function format(string $format): string; 47 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slince/china", 3 | "description": "中国资料库查询大全", 4 | "keywords": ["中国", "China", "节日", "地区"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "slince", 9 | "email": "taosikai@yeah.net" 10 | } 11 | ], 12 | "require": { 13 | "php": "^7.2||^8.0", 14 | "ext-json": "*", 15 | "symfony/console": "^4.2|^5.0|^6.0", 16 | "doctrine/collections": "^1.0|^2.0", 17 | "slince/config": "^1.1", 18 | "slince/di": "^3.0" 19 | }, 20 | "require-dev": { 21 | "phpunit/phpunit": "^7.5|^8.0|^9.0", 22 | "fabpot/goutte": "^4.0", 23 | "symfony/filesystem": "^4.2|^5.0|^6.0" 24 | }, 25 | "autoload":{ 26 | "psr-4": { 27 | "China\\": "src/" 28 | } 29 | }, 30 | "autoload-dev":{ 31 | "psr-4": { 32 | "China\\Tests\\": "tests/" 33 | } 34 | }, 35 | "bin": [ 36 | "bin/china" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /doc/1-basic.md: -------------------------------------------------------------------------------- 1 | ## 安装 2 | 3 | 执行下面命令安装 4 | 5 | ```bash 6 | $ composer require slince/china 7 | ``` 8 | 9 | ## Basic Usage 10 | 11 | ### 节日查询 12 | 13 | ```php 14 | 15 | $china = new China(); 16 | 17 | $holidayService = $china->getHoliday(); 18 | 19 | print_r($holidayService->findAll()); 20 | 21 | ``` 22 | 按名称查找节日 23 | 24 | ```php 25 | $yuandan = $holidayService->find('元旦节'): 26 | 27 | echo $yuandan->getDate(); //输出 1月1日 28 | ``` 29 | 30 | ### 民族查询 31 | 32 | ```php 33 | 34 | $china = new China(); 35 | 36 | $nationalityService = $chna->getNationality(); 37 | 38 | print_r($nationalityService->findAll()); 39 | 40 | ``` 41 | 42 | 按名称查找 43 | 44 | ```php 45 | $hezhe = $nationalityService->find('赫哲族'): 46 | 47 | echo $hezhe->getPinyin(); //输出“hè zhé zú” 48 | echo $hezhe->getPopulation(); //输出人口 49 | ``` 50 | 51 | 52 | ### 地址库查询 53 | 54 | 55 | ```php 56 | 57 | $china = new China(); 58 | 59 | $regionService = $chna->getRegion(); 60 | 61 | print_r($regionService->getProvinces()); //获取树形省市县地区结构 62 | ``` 63 | -------------------------------------------------------------------------------- /tests/Region/AddressFactoryTest.php: -------------------------------------------------------------------------------- 1 | 340000, 15 | 'name' => '安徽省', 16 | 'type' => AddressInterface::TYPE_PROVINCE, 17 | ]); 18 | $bengbu = Address::createFromArray([ 19 | 'code' => 340320, 20 | 'name' => '蚌埠市', 21 | 'type' => AddressInterface::TYPE_CITY, 22 | ], $anhui); 23 | $this->assertEquals($anhui, $bengbu->getParent()); 24 | } 25 | 26 | public function testCreateWithoutType() 27 | { 28 | $this->expectException(\InvalidArgumentException::class); 29 | Address::createFromArray([ 30 | 'code' => 340000, 31 | 'name' => '安徽省', 32 | ]); 33 | } 34 | } -------------------------------------------------------------------------------- /tests/Region/Location/AddressTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(340000, $province->getCode()); 16 | $this->assertEquals('安徽省', $province->getName()); 17 | $this->assertEquals(null, $province->getParent()); 18 | 19 | $city = new City('340300', '蚌埠市', $province); 20 | $this->assertEquals(340300, $city->getCode()); 21 | $this->assertEquals('蚌埠市', $city->getName()); 22 | $this->assertEquals($province, $city->getParent()); 23 | 24 | $area = new District('340322', '五河县', $city); 25 | $this->assertEquals(340322, $area->getCode()); 26 | $this->assertEquals('五河县', $area->getName()); 27 | $this->assertEquals($city, $area->getParent()); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Region/RegionLoader.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region; 15 | 16 | use China\Common\ResourceLoader\LazyLoader; 17 | use China\Region\Location\Address; 18 | use China\Region\Location\AddressInterface; 19 | 20 | class RegionLoader extends LazyLoader 21 | { 22 | 23 | protected function createRecords(array $records): array 24 | { 25 | return [$this->createRecord($records)]; 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | public function createRecord(array $record) 32 | { 33 | $rootData = [ 34 | 'type' => AddressInterface::TYPE_PROVINCE, 35 | 'name' => 'China', 36 | 'code' => 0, 37 | 'children' => $record, 38 | ]; 39 | return Address::createFromArray($rootData); 40 | } 41 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2017 Slince https://www.phpdish.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | XDEBUG_MODE: coverage 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Validate composer.json and composer.lock 21 | run: composer validate --strict 22 | 23 | - name: Cache Composer packages 24 | id: composer-cache 25 | uses: actions/cache@v2 26 | with: 27 | path: vendor 28 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-php- 31 | 32 | - name: Install dependencies 33 | run: composer install --prefer-dist --no-progress 34 | 35 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" 36 | # Docs: https://getcomposer.org/doc/articles/scripts.md 37 | 38 | - name: Run test suite 39 | run: ./vendor/bin/phpunit --coverage-clover coverage.xml 40 | 41 | - name: Upload coverage to Codecov 42 | uses: codecov/codecov-action@v1 43 | with: 44 | token: ${{ secrets.CODECOV_TOKEN }} 45 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: 3 | - "tests/*" 4 | - "vendor/*" 5 | 6 | tools: 7 | external_code_coverage: 8 | timeout: 600 9 | 10 | php_sim: true 11 | 12 | php_changetracking: true 13 | 14 | php_cs_fixer: 15 | enabled: true 16 | config: 17 | level: all 18 | filter: 19 | excluded_paths: 20 | - "tests/*" 21 | - "vendor/*" 22 | 23 | php_mess_detector: 24 | enabled: true 25 | filter: 26 | excluded_paths: 27 | - "tests/*" 28 | - "vendor/*" 29 | 30 | php_pdepend: 31 | enabled: true 32 | filter: 33 | excluded_paths: 34 | - "tests/*" 35 | - "vendor/*" 36 | 37 | php_analyzer: 38 | enabled: true 39 | filter: 40 | excluded_paths: 41 | - "tests/*" 42 | - "vendor/*" 43 | 44 | 45 | php_cpd: 46 | enabled: true 47 | excluded_dirs: 48 | - "tests/*" 49 | - "vendor/*" 50 | 51 | php_loc: 52 | enabled: true 53 | excluded_dirs: 54 | - "tests/*" 55 | - "vendor/*" -------------------------------------------------------------------------------- /src/Holiday/HolidayInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | interface HolidayInterface extends \JsonSerializable 17 | { 18 | /** 19 | * 节日类型,中国传统节日. 20 | * 21 | * @var string 22 | */ 23 | const TYPE_TRADITIONAL = 'traditional'; 24 | 25 | /** 26 | * 节日类型,中国24节气. 27 | * 28 | * @var string 29 | */ 30 | const TYPE_SOLAR_TERM = 'solar_term'; 31 | 32 | /** 33 | * 节日类型,国际节日. 34 | * 35 | * @var string 36 | */ 37 | const TYPE_INTERNATIONAL = 'international'; 38 | 39 | /** 40 | * 获取名称. 41 | * 42 | * @return string 43 | */ 44 | public function getName(): string; 45 | 46 | /** 47 | * 获取类型. 48 | * 49 | * @return string 50 | */ 51 | public function getType(): string; 52 | 53 | /** 54 | * 获取日期 55 | * 56 | * @return DateInterface 57 | */ 58 | public function getDate(): DateInterface; 59 | } -------------------------------------------------------------------------------- /src/Nationality/NationalityService.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Nationality; 15 | 16 | use China\Common\ResourceFile; 17 | use Doctrine\Common\Collections\Collection; 18 | 19 | class NationalityService implements NationalityServiceInterface 20 | { 21 | /** 22 | * @var Collection 23 | */ 24 | protected $nationalities; 25 | 26 | public function __construct(ResourceFile $resourceFile) 27 | { 28 | $this->nationalities = new NationalityLoader($resourceFile); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function findAll() 35 | { 36 | return $this->nationalities; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function find($name) 43 | { 44 | return $this->nationalities->filter(function(NationalityInterface $nationality) use ($name){ 45 | return $nationality->getName() === $name; 46 | })->first(); 47 | } 48 | } -------------------------------------------------------------------------------- /doc/2-region.md: -------------------------------------------------------------------------------- 1 | ## 国内省市县地区查询 2 | 3 | ### 获取地区查询服务 4 | 5 | 所有的服务获取均通过 `China\China`对象; 6 | 7 | ```php 8 | $china = new China\China(); 9 | 10 | $regionService = $china->getRegion(); 11 | 12 | $regionService->getProvinces(); // 获取所有的省(树形层级结构) 13 | ``` 14 | 15 | ### 基本查询 16 | 17 | - `findByName` 按照名称查询 18 | - `findByCode` 按照地区码 19 | - `filter` 按照自定义的闭包查询 20 | 21 | 22 | ```php 23 | $anhui = $regionService->findByName('安徽省'): 24 | $beijing = $regionService->findByName('110000'): //根据地区码 25 | ``` 26 | 27 | ### 地区的链式结构 28 | 29 | 通过`AddressInterface`的`getParent()`和`getChildren()`方法你可以自由地查找当前地区的上级或者下级地区; 30 | 需要注意的是下级地区集合是`RegionCollectionInterfaec`实例,该实例支持上述的三条基本查询; 31 | 32 | ```php 33 | $bengbushi = $regionService->findByName('蚌埠市'); 34 | echo $bengbushi->getParent()->getName(); //输出蚌埠市所属省, 安徽省 35 | $yuhuiqu = $bengbushi->getChildren()->findByName('禹会区'); //获取蚌埠市下属禹会区 36 | $yuhuiqu->getParent()->getChildren()->findByName('淮上区'); //获取禹会区同级淮上区 37 | ``` 38 | 39 | ### 查找身份证归属地 40 | 41 | ```php 42 | $huaiyuanxian = $regionService->findByIdCard('340321199212026972'); //怀远县 43 | ``` 44 | 注意,你需要提供一个完整有效的身份证号码;查找出来的地区定位到县级地区;你可以通过地区的链式方法获取到省市级地区; 45 | 46 | ```php 47 | $city = $huaiyuanxian->getParent(); //蚌埠市 48 | $province = $huaiyuanxian->getParent()->getParent(); //安徽省 49 | ``` -------------------------------------------------------------------------------- /src/Command/CrawlCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Common\FilesystemAwareInterface; 17 | use China\Common\FilesystemAwareTrait; 18 | use Goutte\Client; 19 | use Symfony\Component\Console\Command\Command; 20 | 21 | class CrawlCommand extends Command implements FilesystemAwareInterface 22 | { 23 | use FilesystemAwareTrait; 24 | 25 | /** 26 | * @var string 27 | */ 28 | protected $resourceDir; 29 | 30 | /** 31 | * @var Client 32 | */ 33 | protected $client; 34 | 35 | public function __construct(string $resourceDir) 36 | { 37 | $this->resourceDir = $resourceDir; 38 | parent::__construct(); 39 | } 40 | 41 | /** 42 | * Goutte 实例 43 | * @return Client 44 | */ 45 | public function getClient(): Client 46 | { 47 | if ($this->client) { 48 | return $this->client; 49 | } 50 | return $this->client = new Client(); 51 | } 52 | } -------------------------------------------------------------------------------- /src/Region/AddressFinderTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region; 15 | 16 | use China\Region\Location\AddressInterface; 17 | 18 | trait AddressFinderTrait 19 | { 20 | /** 21 | * 根据Code查找地址 22 | * 23 | * @param string $code 24 | * 25 | * @return AddressInterface|null 26 | */ 27 | public function findByCode(string $code): ?AddressInterface 28 | { 29 | $result = $this->filter(function(AddressInterface $address) use ($code){ 30 | return $address->getCode() == $code; 31 | })->first(); 32 | return $result ?: null; 33 | } 34 | 35 | /** 36 | * 根据Name查找地址 37 | * 38 | * @param string $name 39 | * 40 | * @return AddressInterface|null 41 | */ 42 | public function findByName(string $name): ?AddressInterface 43 | { 44 | $result = $this->filter(function(AddressInterface $address) use ($name){ 45 | return $address->getName() == $name; 46 | })->first(); 47 | return $result ?: null; 48 | } 49 | } -------------------------------------------------------------------------------- /tests/IDCard/IDCardTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('340321199212026972', $idCard); 14 | $this->assertEquals('340321199212026972', $idCard->getLongNumber()); 15 | $this->assertFalse($idCard->isShortLength()); 16 | } 17 | 18 | public function testCreateError() 19 | { 20 | try { 21 | $idCard = new IDCard('340321199212026973'); 22 | $this->fail(); 23 | } catch (\Exception $exception) { 24 | $this->assertStringContainsString('invalid', $exception->getMessage()); 25 | } 26 | 27 | try { 28 | $idCard = new IDCard('qwe340321199212026973'); 29 | $this->fail(); 30 | } catch (\Exception $exception) { 31 | $this->assertStringContainsString('length should be 15 numbers or 18 numbers', $exception->getMessage()); 32 | } 33 | 34 | try { 35 | $idCard = new IDCard('34032119921202697'); 36 | $this->fail(); 37 | } catch (\Exception $exception) { 38 | $this->assertStringContainsString('card length should be 15 numbers or 18 numbers', $exception->getMessage()); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Holiday/Holiday.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | final class Holiday implements HolidayInterface 17 | { 18 | /** 19 | * @var string 20 | */ 21 | protected $name; 22 | 23 | /** 24 | * @var string 25 | */ 26 | protected $type; 27 | 28 | protected $date; 29 | 30 | public function __construct(string $name, string $type, DateInterface $date) 31 | { 32 | $this->name = $name; 33 | $this->type = $type; 34 | $this->date = $date; 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | public function getName(): string 41 | { 42 | return $this->name; 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function getType(): string 49 | { 50 | return $this->type; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function getDate(): DateInterface 57 | { 58 | return $this->date; 59 | } 60 | 61 | /** 62 | * {@inheritdoc} 63 | */ 64 | public function jsonSerialize() 65 | { 66 | return [ 67 | 'name' => $this->name, 68 | 'type' => $this->type, 69 | 'date' => $this->date, 70 | ]; 71 | } 72 | } -------------------------------------------------------------------------------- /src/Holiday/HolidayServiceInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | use Doctrine\Common\Collections\Collection; 17 | 18 | interface HolidayServiceInterface 19 | { 20 | /** 21 | * 获取所有的节假日. 22 | * 23 | * @return HolidayInterface[]|Collection 24 | */ 25 | public function findAll(): iterable; 26 | 27 | /** 28 | * 获取所有的传统节日. 29 | * 30 | * @return HolidayInterface[]|Collection 31 | */ 32 | public function findTraditionalHolidays(): iterable; 33 | 34 | /** 35 | * 获取所有的国际节日. 36 | * 37 | * @return HolidayInterface[]|Collection 38 | */ 39 | public function findInternationalHolidays(): iterable; 40 | 41 | /** 42 | * 获取所有的节气. 43 | * 44 | * @return HolidayInterface[]|Collection 45 | */ 46 | public function findSolarTermHolidays(): iterable; 47 | 48 | /** 49 | * 根据类型获取节假日. 50 | * 51 | * @param string $type 52 | * 53 | * @return HolidayInterface[]|Collection 54 | */ 55 | public function findHolidaysByType(string $type): iterable; 56 | 57 | /** 58 | * 查找指定的民族. 59 | * 60 | * @param string $name 61 | * 62 | * @return HolidayInterface 63 | */ 64 | public function find(string $name): ?HolidayInterface; 65 | } -------------------------------------------------------------------------------- /src/Region/Location/AddressInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region\Location; 15 | 16 | use China\Region\RegionCollectionInterface; 17 | 18 | interface AddressInterface extends \JsonSerializable 19 | { 20 | /** 21 | * 类型,省 22 | * 23 | * @return string 24 | */ 25 | const TYPE_PROVINCE = 'province'; 26 | 27 | /** 28 | * 类型,市 29 | * 30 | * @return string 31 | */ 32 | const TYPE_CITY = 'city'; 33 | 34 | /** 35 | * 类型,区县 36 | * 37 | * @return string 38 | */ 39 | const TYPE_DISTRICT = 'district'; 40 | 41 | /** 42 | * 获取Code. 43 | * 44 | * @return int 45 | */ 46 | public function getCode(): int; 47 | 48 | /** 49 | * 获取地址名称. 50 | * 51 | * @return string 52 | */ 53 | public function getName(): string; 54 | 55 | /** 56 | * 获取上一级地址 57 | * 58 | * @return AddressInterface 59 | */ 60 | public function getParent(): ?AddressInterface; 61 | 62 | /** 63 | * 设置子地区 64 | * @param iterable $children 65 | */ 66 | public function setChildren(iterable $children); 67 | 68 | /** 69 | * 获取子地区. 70 | * 71 | * @return RegionCollectionInterface|AddressInterface[] 72 | */ 73 | public function getChildren(): iterable; 74 | } -------------------------------------------------------------------------------- /src/Command/ShowNationalityCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Nationality\NationalityInterface; 17 | use Symfony\Component\Console\Input\InputInterface; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | use Symfony\Component\Console\Style\SymfonyStyle; 20 | 21 | class ShowNationalityCommand extends DashboardCommand 22 | { 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function configure() 27 | { 28 | $this->setName('view:nationality'); 29 | $this->setDescription('展示民族数据信息'); 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | public function execute(InputInterface $input, OutputInterface $output) 36 | { 37 | $nationalityService = $this->getChina()->getNationality(); 38 | $style = new SymfonyStyle($input, $output); 39 | $headers = ['名称', '拼音', '人口']; 40 | 41 | $nationalities = $nationalityService->findAll()->toArray(); 42 | $rows = array_map(function(NationalityInterface $nationality){ 43 | return [ 44 | "{$nationality->getName()}", 45 | $nationality->getPinyin(), 46 | $nationality->getPopulation(), 47 | ]; 48 | }, $nationalities); 49 | $style->table($headers, $rows); 50 | return 0; 51 | } 52 | } -------------------------------------------------------------------------------- /src/Region/RegionServiceInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region; 15 | 16 | use China\IDCard\IDCardInterface; 17 | use China\Region\Location\AddressInterface; 18 | use China\Region\Location\Province; 19 | use Doctrine\Common\Collections\Collection; 20 | 21 | interface RegionServiceInterface 22 | { 23 | /** 24 | * 获取所有省份. 25 | * 26 | * @return Province[]|Collection 27 | */ 28 | public function getProvinces(): iterable; 29 | 30 | /** 31 | * 查找身份证号码归属地. 32 | * 33 | * @param string|IDCardInterface $idCard 34 | * 35 | * @return AddressInterface|null 36 | */ 37 | public function findByIdCard($idCard): ?AddressInterface; 38 | 39 | /** 40 | * 筛选地区. 41 | * 42 | * @param \Closure $callback 43 | * 44 | * @return AddressInterface[]|RegionCollectionInterface 45 | */ 46 | public function filter(\Closure $callback): iterable; 47 | 48 | /** 49 | * 根据Code查找地址 50 | * 51 | * @param string $code 52 | * 53 | * @return AddressInterface|null 54 | */ 55 | public function findByCode(string $code): ?AddressInterface; 56 | 57 | /** 58 | * 根据Name查找地址 59 | * 60 | * @param string $name 61 | * 62 | * @return AddressInterface|null 63 | */ 64 | public function findByName(string $name): ?AddressInterface; 65 | } -------------------------------------------------------------------------------- /src/Nationality/Nationality.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Nationality; 15 | 16 | final class Nationality implements NationalityInterface 17 | { 18 | /** 19 | * @var string 20 | */ 21 | protected $name; 22 | 23 | /** 24 | * @var string 25 | */ 26 | protected $pinyin; 27 | 28 | /** 29 | * @var number 30 | */ 31 | protected $population; 32 | 33 | public function __construct(string $name, string $pinyin, int $population) 34 | { 35 | $this->name = $name; 36 | $this->pinyin = $pinyin; 37 | $this->population = $population; 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public function getName(): string 44 | { 45 | return $this->name; 46 | } 47 | 48 | /** 49 | * {@inheritdoc} 50 | */ 51 | public function getPinyin(): string 52 | { 53 | return $this->pinyin; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function getPopulation(): int 60 | { 61 | return $this->population; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function jsonSerialize() 68 | { 69 | return [ 70 | 'name' => $this->name, 71 | 'pinyin' => $this->pinyin, 72 | 'population' => $this->population, 73 | ]; 74 | } 75 | } -------------------------------------------------------------------------------- /tests/Region/RegionCollectionTest.php: -------------------------------------------------------------------------------- 1 | 340000, 16 | 'name' => '安徽省', 17 | 'type' => AddressInterface::TYPE_PROVINCE, 18 | ]); 19 | $bengbu = Address::createFromArray([ 20 | 'code' => 340320, 21 | 'name' => '蚌埠市', 22 | 'type' => AddressInterface::TYPE_CITY, 23 | ], $anhui); 24 | 25 | return new RegionCollection([ 26 | $anhui, 27 | $bengbu, 28 | ]); 29 | } 30 | 31 | public function testFindByName() 32 | { 33 | $regions = $this->createCollection(); 34 | $this->assertEquals('安徽省', $regions->findByName('安徽省')->getName()); 35 | $this->assertEquals(340000, $regions->findByName('安徽省')->getCode()); 36 | } 37 | 38 | public function testFindByCode() 39 | { 40 | $regions = $this->createCollection(); 41 | $this->assertEquals('蚌埠市', $regions->findByCode(340320)->getName()); 42 | $this->assertEquals(340320, $regions->findByCode(340320)->getCode()); 43 | } 44 | 45 | public function testFilter() 46 | { 47 | $regions = $this->createCollection()->filter(function(AddressInterface $address){ 48 | return strpos($address->getCode(), '340') === 0; 49 | }); 50 | $this->assertCount(2, $regions); 51 | } 52 | } -------------------------------------------------------------------------------- /src/Command/Application.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Common\FilesystemAwareInterface; 17 | use Symfony\Component\Console\Application as BaseApplication; 18 | use Symfony\Component\Filesystem\Filesystem; 19 | 20 | final class Application extends BaseApplication 21 | { 22 | /** 23 | * @var string 24 | */ 25 | protected $resourceDir; 26 | 27 | public function __construct(string $resourceDir) 28 | { 29 | $this->resourceDir = $resourceDir; 30 | parent::__construct('China'); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | protected function getDefaultCommands(): array 37 | { 38 | return array_merge(parent::getDefaultCommands(), $this->createCommands()); 39 | } 40 | 41 | protected function createCommands(): array 42 | { 43 | $commands = [ 44 | new CrawlHolidayCommand($this->resourceDir), 45 | new CrawlNationalityCommand($this->resourceDir), 46 | new CrawlRegionCommand($this->resourceDir), 47 | new ShowHolidayCommand(), 48 | new ShowNationalityCommand(), 49 | new ShowRegionCommand(), 50 | ]; 51 | $filesystem = new Filesystem(); 52 | foreach ($commands as $command) { 53 | if ($command instanceof FilesystemAwareInterface) { 54 | $command->setFilesystem($filesystem); 55 | } 56 | } 57 | return $commands; 58 | } 59 | } -------------------------------------------------------------------------------- /src/Common/ResourceLoader/LazyLoader.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Common\ResourceLoader; 15 | 16 | use China\Common\ResourceFile; 17 | use Doctrine\Common\Collections\AbstractLazyCollection; 18 | use Doctrine\Common\Collections\ArrayCollection; 19 | use Slince\Config\Config; 20 | 21 | class LazyLoader extends AbstractLazyCollection implements ResourceLoaderInterface 22 | { 23 | /** 24 | * @var ResourceFile 25 | */ 26 | protected $resourceFile; 27 | 28 | public function __construct(ResourceFile $resourceFile) 29 | { 30 | $this->resourceFile = $resourceFile; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function getResourceFile(): ResourceFile 37 | { 38 | return $this->resourceFile; 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function doInitialize() 45 | { 46 | $data = (new Config($this->resourceFile->getPathname()))->toArray(); 47 | $this->collection = new ArrayCollection($this->createRecords($data)); 48 | } 49 | 50 | protected function createRecords(array $records): array 51 | { 52 | $processed = []; 53 | foreach ($records as $record) { 54 | $processed[] = $this->createRecord($record); 55 | } 56 | return $processed; 57 | } 58 | 59 | /** 60 | * {@inheritdoc} 61 | */ 62 | public function createRecord(array $record) 63 | { 64 | return $record; 65 | } 66 | } -------------------------------------------------------------------------------- /src/IDCard/IDCard.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\IDCard; 15 | 16 | class IDCard implements IDCardInterface 17 | { 18 | /** 19 | * 身份证数字. 20 | * 21 | * @var string 22 | */ 23 | protected $id; 24 | 25 | public function __construct(string $id) 26 | { 27 | self::assertValidIDCard($id); 28 | $this->id = $id; 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function __toString() 35 | { 36 | return $this->id; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function isShortLength(): bool 43 | { 44 | return strlen($this->id) === 15; 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function getLongNumber(): string 51 | { 52 | $id = $this->id; 53 | if ($this->isShortLength()) { 54 | $id = IDCardUtils::convertIDCard15to18($this->id); 55 | } 56 | 57 | return $id; 58 | } 59 | 60 | /** 61 | * 身份证是否合法. 62 | * 63 | * @param string $id 64 | */ 65 | public static function assertValidIDCard(string $id) 66 | { 67 | $length = strlen($id); 68 | if ($length !== 15 && $length !== 18) { 69 | throw new \InvalidArgumentException(sprintf('The id "%s" card length should be 15 numbers or 18 numbers, given %d', $id, $length)); 70 | } 71 | $convertedId = $id; 72 | if ($length === 15) { 73 | $convertedId = IDCardUtils::convertIDCard15to18($id); 74 | } 75 | if (!IDCardUtils::check18IDCard($convertedId)) { 76 | throw new \InvalidArgumentException(sprintf('The Id card "%s" is invalid', $id)); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /resources/regions/provinces.json: -------------------------------------------------------------------------------- 1 | [{"code":"110000","name":"北京市","type":"province","children":[]},{"code":"120000","name":"天津市","type":"province","children":[]},{"code":"130000","name":"河北省","type":"province","children":[]},{"code":"140000","name":"山西省","type":"province","children":[]},{"code":"150000","name":"内蒙古自治区","type":"province","children":[]},{"code":"210000","name":"辽宁省","type":"province","children":[]},{"code":"220000","name":"吉林省","type":"province","children":[]},{"code":"230000","name":"黑龙江省","type":"province","children":[]},{"code":"310000","name":"上海市","type":"province","children":[]},{"code":"320000","name":"江苏省","type":"province","children":[]},{"code":"330000","name":"浙江省","type":"province","children":[]},{"code":"340000","name":"安徽省","type":"province","children":[]},{"code":"350000","name":"福建省","type":"province","children":[]},{"code":"360000","name":"江西省","type":"province","children":[]},{"code":"370000","name":"山东省","type":"province","children":[]},{"code":"410000","name":"河南省","type":"province","children":[]},{"code":"420000","name":"湖北省","type":"province","children":[]},{"code":"430000","name":"湖南省","type":"province","children":[]},{"code":"440000","name":"广东省","type":"province","children":[]},{"code":"450000","name":"广西壮族自治区","type":"province","children":[]},{"code":"460000","name":"海南省","type":"province","children":[]},{"code":"500000","name":"重庆市","type":"province","children":[]},{"code":"510000","name":"四川省","type":"province","children":[]},{"code":"520000","name":"贵州省","type":"province","children":[]},{"code":"530000","name":"云南省","type":"province","children":[]},{"code":"540000","name":"西藏自治区","type":"province","children":[]},{"code":"610000","name":"陕西省","type":"province","children":[]},{"code":"620000","name":"甘肃省","type":"province","children":[]},{"code":"630000","name":"青海省","type":"province","children":[]},{"code":"640000","name":"宁夏回族自治区","type":"province","children":[]},{"code":"650000","name":"新疆维吾尔自治区","type":"province","children":[]},{"code":"710000","name":"台湾省","type":"province","children":[]},{"code":"810000","name":"香港特别行政区","type":"province","children":[]},{"code":"820000","name":"澳门特别行政区","type":"province","children":[]}] -------------------------------------------------------------------------------- /tests/Region/RegionServiceTest.php: -------------------------------------------------------------------------------- 1 | getChina()->getRegion(); 12 | $provinces = $regionService->getProvinces(); 13 | $this->assertCount(34, $provinces); 14 | $this->assertEquals('北京市', $provinces->first()->getName()); 15 | } 16 | 17 | public function testFindByCode() 18 | { 19 | $regionService = $this->getChina()->getRegion(); 20 | $beijing = $regionService->findByCode('110000'); 21 | $this->assertEquals('北京市', $beijing->getName()); 22 | } 23 | 24 | public function testFindByName() 25 | { 26 | $regionService = $this->getChina()->getRegion(); 27 | $beijing = $regionService->findByName('江宁区'); 28 | $this->assertEquals('320115', $beijing->getCode()); 29 | } 30 | 31 | public function testFind() 32 | { 33 | $regionService = $this->getChina()->getRegion(); 34 | $this->assertNull($regionService->findByName('不存在的地区')); 35 | $bengbu = $regionService->findByName('蚌埠市'); 36 | $this->assertEquals($regionService->findByName('安徽省'), $bengbu->getParent()); 37 | $this->assertContains($regionService->findByName('怀远县'), $bengbu->getChildren()); 38 | } 39 | 40 | public function testFindByIdCard() 41 | { 42 | $regionService = $this->getChina()->getRegion(); 43 | $address = $regionService->findByIdCard('340321199106196978'); 44 | $this->assertEquals($regionService->findByName('怀远县'), $address); 45 | $this->assertEquals($regionService->findByName('蚌埠市'), $address->getParent()); 46 | $this->assertEquals($regionService->findByName('安徽省'), $address->getParent()->getParent()); 47 | } 48 | 49 | public function testFindChain() 50 | { 51 | $regionService = $this->getChina()->getRegion(); 52 | $anhui = $regionService->findByName('安徽省'); 53 | $this->assertCount(16, $anhui->getChildren()); 54 | $this->assertEquals($anhui, $anhui->getChildren()->first()->getParent()); 55 | } 56 | } -------------------------------------------------------------------------------- /src/Holiday/HolidayService.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | use China\Common\ResourceFile; 17 | use Doctrine\Common\Collections\Collection; 18 | 19 | class HolidayService implements HolidayServiceInterface 20 | { 21 | /** 22 | * @var Collection 23 | */ 24 | protected $holidays; 25 | 26 | public function __construct(ResourceFile $resourceFile) 27 | { 28 | $this->holidays = new HolidayLoader($resourceFile); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function findAll(): iterable 35 | { 36 | return $this->holidays; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function findTraditionalHolidays(): iterable 43 | { 44 | return $this->findHolidaysByType(HolidayInterface::TYPE_TRADITIONAL); 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function findInternationalHolidays(): iterable 51 | { 52 | return $this->findHolidaysByType(HolidayInterface::TYPE_INTERNATIONAL); 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function findSolarTermHolidays(): iterable 59 | { 60 | return $this->findHolidaysByType(HolidayInterface::TYPE_SOLAR_TERM); 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function findHolidaysByType(string $type): iterable 67 | { 68 | return $this->holidays->filter(function(HolidayInterface $holiday) use ($type){ 69 | return $holiday->getType() === $type; 70 | }); 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function find(string $name): ?HolidayInterface 77 | { 78 | $result = $this->holidays->filter(function(HolidayInterface $holiday) use ($name){ 79 | return $holiday->getName() === $name; 80 | })->first(); 81 | return $result ?: null; 82 | } 83 | } -------------------------------------------------------------------------------- /src/Command/ShowRegionCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Region\Location\AddressInterface; 17 | use Symfony\Component\Console\Input\InputInterface; 18 | use Symfony\Component\Console\Input\InputOption; 19 | use Symfony\Component\Console\Output\OutputInterface; 20 | use Symfony\Component\Console\Style\SymfonyStyle; 21 | 22 | class ShowRegionCommand extends DashboardCommand 23 | { 24 | protected static $types = [ 25 | AddressInterface::TYPE_PROVINCE => '省', 26 | AddressInterface::TYPE_CITY => '市', 27 | AddressInterface::TYPE_DISTRICT => '区 ', 28 | ]; 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function configure() 34 | { 35 | $this->setName('view:region'); 36 | $this->addOption('parent', 'p', InputOption::VALUE_OPTIONAL); 37 | $this->setDescription('展示我国省市县信息'); 38 | } 39 | 40 | /** 41 | * {@inheritdoc} 42 | */ 43 | public function execute(InputInterface $input, OutputInterface $output) 44 | { 45 | $regionService = $this->getChina()->getRegion(); 46 | $style = new SymfonyStyle($input, $output); 47 | $headers = ['Code', '名称', '类型']; 48 | 49 | if ($parentName = $input->getOption('parent')) { 50 | $parent = $regionService->findByName($parentName) ?: $regionService->findByCode($parentName); 51 | if ($parent === false) { 52 | throw new \InvalidArgumentException(sprintf('你提供的上级地区“%s”似乎是不存在的', $parentName)); 53 | } 54 | $regions = $parent->getChildren()->toArray(); 55 | } else { 56 | $regions = $regionService->getProvinces()->toArray(); 57 | } 58 | 59 | $rows = array_map(function(AddressInterface $address){ 60 | return [ 61 | $address->getCode(), 62 | "{$address->getName()}", 63 | static::$types[$address->getType()], 64 | ]; 65 | }, $regions); 66 | $style->table($headers, $rows); 67 | return 0; 68 | } 69 | } -------------------------------------------------------------------------------- /src/IDCard/IDCardUtils.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\IDCard; 15 | 16 | /** 17 | * 算法摘自 18 | * {@link https://www.cnblogs.com/bossikill/p/3679926.html}. 19 | */ 20 | final class IDCardUtils 21 | { 22 | /** 23 | * 计算身份证最后一位. 24 | * 25 | * @param string $idCardBody 26 | * 27 | * @return bool|string 28 | */ 29 | public static function calcIDCardCode(string $idCardBody) 30 | { 31 | if (strlen($idCardBody) != 17) { 32 | return false; 33 | } 34 | //加权因子 35 | $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); 36 | //校验码对应值 37 | $code = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); 38 | $checksum = 0; 39 | 40 | for ($i = 0; $i < strlen($idCardBody); ++$i) { 41 | $checksum += substr($idCardBody, $i, 1) * $factor[$i]; 42 | } 43 | 44 | return $code[$checksum % 11]; 45 | } 46 | 47 | /** 48 | * 将15位身份证升级到18位. 49 | * 50 | * @param string $id 51 | * 52 | * @return bool|string 53 | */ 54 | public static function convertIDCard15to18(string $id) 55 | { 56 | if (strlen($id) != 15) { 57 | return false; 58 | } else { 59 | // 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码 60 | if (array_search(substr($id, 12, 3), array('996', '997', '998', '999')) !== false) { 61 | $id = substr($id, 0, 6).'18'.substr($id, 6, 9); 62 | } else { 63 | $id = substr($id, 0, 6).'19'.substr($id, 6, 9); 64 | } 65 | } 66 | 67 | return $id . self::calcIDCardCode($id); 68 | } 69 | 70 | /** 71 | * 18位身份证校验码有效性检查. 72 | * 73 | * @param string $idCard 74 | * 75 | * @return bool 76 | */ 77 | public static function check18IDCard(string $idCard): bool 78 | { 79 | if (strlen($idCard) != 18) { 80 | return false; 81 | } 82 | $idCardBody = substr($idCard, 0, 17); //身份证主体 83 | $idCardCode = strtoupper(substr($idCard, 17, 1)); //身份证最后一位的验证码 84 | 85 | return self::calcIDCardCode($idCardBody) === $idCardCode; 86 | } 87 | } -------------------------------------------------------------------------------- /src/Holiday/Date.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Holiday; 15 | 16 | final class Date implements DateInterface 17 | { 18 | /** 19 | * 月. 20 | * 21 | * @var int 22 | */ 23 | protected $month; 24 | 25 | /** 26 | * 天. 27 | * 28 | * @var int 29 | */ 30 | protected $day; 31 | 32 | public function __construct(int $month, int $day) 33 | { 34 | $this->setMonth($month); 35 | $this->setDay($day); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function getDay(): int 42 | { 43 | return $this->day; 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function getMonth(): int 50 | { 51 | return $this->month; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function format(string $format): string 58 | { 59 | return str_replace(['{month}', '{day}',], [ 60 | $this->month, 61 | $this->day, 62 | ], $format); 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function toString(): string 69 | { 70 | return $this->format('{month}月{day}日'); 71 | } 72 | 73 | /** 74 | * 字符串输出. 75 | * 76 | * @return string 77 | */ 78 | public function __toString() 79 | { 80 | return $this->toString(); 81 | } 82 | 83 | /** 84 | * {@inheritdoc} 85 | */ 86 | public function jsonSerialize() 87 | { 88 | return $this->toString(); 89 | } 90 | 91 | protected function setMonth(int $month) 92 | { 93 | if (!is_numeric($month) || $month < 1 || $month > 12) { 94 | throw new \InvalidArgumentException(sprintf('Wrong month, given "%s"', $month)); 95 | } 96 | $this->month = $month; 97 | } 98 | 99 | protected function setDay($day) 100 | { 101 | if (!is_numeric($day) || $day < 1 || $day > 31) { 102 | throw new \InvalidArgumentException(sprintf('Wrong day, given "%s"', $day)); 103 | } 104 | $this->day = $day; 105 | } 106 | } -------------------------------------------------------------------------------- /src/Region/RegionService.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region; 15 | 16 | use China\Common\ResourceFile; 17 | use China\IDCard\IDCard; 18 | use China\Region\Location\AddressInterface; 19 | use Doctrine\Common\Collections\Collection; 20 | 21 | class RegionService implements RegionServiceInterface 22 | { 23 | use AddressFinderTrait; 24 | 25 | /** 26 | * @var AddressInterface 27 | */ 28 | protected $regions; 29 | 30 | /** 31 | * 根据code做索引. 32 | * 33 | * @var AddressInterface[]|Collection 34 | */ 35 | protected $flattenRegions; 36 | 37 | public function __construct(ResourceFile $file) 38 | { 39 | $this->regions = new RegionLoader($file); 40 | $this->flattenRegions = new RegionCollection(); 41 | } 42 | 43 | /** 44 | * {@inheritdoc} 45 | */ 46 | public function getProvinces(): iterable 47 | { 48 | return $this->regions->first()->getChildren(); 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function findByIdCard($idCard): ?AddressInterface 55 | { 56 | if (is_string($idCard)) { 57 | $idCard = new IDCard($idCard); 58 | } 59 | $areaCode = substr((string)$idCard, 0, 6); 60 | 61 | return $this->findByCode($areaCode); 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function filter(\Closure $callback): iterable 68 | { 69 | if (!$this->flattenRegions->isEmpty()) { 70 | return $this->flattenRegions->filter($callback); 71 | } 72 | // 没有遍历过则自行遍历 73 | $addresses = []; 74 | $this->traverseTree($this->regions->first(), function(AddressInterface $address) use ($callback, &$addresses){ 75 | if ($callback($address) === true) { 76 | $addresses[] = $address; 77 | } 78 | }); 79 | return new RegionCollection($addresses); 80 | } 81 | 82 | /** 83 | * 遍历地区树. 84 | * 85 | * @param AddressInterface $address 86 | * @param \Closure $callback 87 | */ 88 | protected function traverseTree(AddressInterface $address, \Closure $callback) 89 | { 90 | $callback($address); 91 | //将树形结构扁平化 92 | $this->flattenRegions->add($address); 93 | if ($address->getChildren()) { 94 | foreach ($address->getChildren() as $child) { 95 | $this->traverseTree($child, $callback); 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/Command/CrawlHolidayCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Holiday\Date; 17 | use China\Holiday\Holiday; 18 | use China\Holiday\HolidayInterface; 19 | use Symfony\Component\Console\Input\InputInterface; 20 | use Symfony\Component\Console\Output\OutputInterface; 21 | use Symfony\Component\Console\Style\SymfonyStyle; 22 | use Symfony\Component\DomCrawler\Crawler; 23 | 24 | class CrawlHolidayCommand extends CrawlCommand 25 | { 26 | /** 27 | * 节假日资源地址 28 | * 29 | * @var string 30 | */ 31 | const URL = 'http://www.sojson.com/time/holiday.html'; 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function configure() 37 | { 38 | $this->setName('crawl:holiday'); 39 | $this->setDescription('采集节假日数据'); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function execute(InputInterface $input, OutputInterface $output) 46 | { 47 | $style = new SymfonyStyle($input, $output); 48 | 49 | $outputFile = $this->resourceDir.'/holidays.json'; 50 | 51 | $crawler = $this->getClient()->request('GET', static::URL); 52 | $holidays = $crawler->filter('.festival_list')->each(function(Crawler $node){ 53 | $fontNode = $node->filter('font'); 54 | $date = $this->parseToDate(strstr($node->text(), '[')); 55 | $date = new Date($date[0], $date[1]); 56 | $type = $this->convertColorToType($fontNode->attr('color')); 57 | 58 | return new Holiday($fontNode->text(), $type, $date); 59 | }); 60 | 61 | $this->filesystem->dumpFile($outputFile, \json_encode($holidays, JSON_UNESCAPED_UNICODE)); 62 | 63 | $style->writeln(sprintf('Crawl completed, please check the file at "%s"', realpath($outputFile))); 64 | return 0; 65 | } 66 | 67 | protected function parseToDate(string $dateString) 68 | { 69 | return explode('/', trim($dateString, '][')); 70 | } 71 | 72 | protected function convertColorToType(string $class): string 73 | { 74 | $type = ''; 75 | switch ($class) { 76 | case 'red': 77 | $type = HolidayInterface::TYPE_TRADITIONAL; 78 | break; 79 | 80 | case 'green': 81 | $type = HolidayInterface::TYPE_INTERNATIONAL; 82 | break; 83 | 84 | case 'blue': 85 | $type = HolidayInterface::TYPE_SOLAR_TERM; 86 | break; 87 | } 88 | 89 | return $type; 90 | } 91 | } -------------------------------------------------------------------------------- /resources/nationalities.json: -------------------------------------------------------------------------------- 1 | [{"name":"阿昌族","pinyin":"ā chāng zú","population":"39,555"},{"name":"鄂温克族","pinyin":"è wēn kè zú","population":"30,875"},{"name":"傈僳族","pinyin":"lì sù zú","population":"702,839"},{"name":"水族","pinyin":"shuǐ zú","population":"411,847"},{"name":"白族","pinyin":"bái zú","population":"1,933,510"},{"name":"高山族","pinyin":"gāo shān zú","population":"4,009"},{"name":"珞巴族","pinyin":"luò bā zú","population":"3,682"},{"name":"塔吉克族","pinyin":"tǎ jí kè zú","population":"51,069"},{"name":"保安族","pinyin":"bǎo ān zú","population":"20,074"},{"name":"仡佬族","pinyin":"gě lǎo zú","population":"550,746"},{"name":"满族","pinyin":"mǎn zú","population":"10,387,958"},{"name":"塔塔尔族","pinyin":"tǎ tǎ ěr zú","population":"3,556"},{"name":"布朗族","pinyin":"bù lǎng zú","population":"119,639"},{"name":"哈尼族","pinyin":"hā ní zú","population":"1,660,932"},{"name":"毛南族","pinyin":"máo nán zú","population":"101,192"},{"name":"土家族","pinyin":"tǔ jiā zú","population":"8,353,912"},{"name":"布依族","pinyin":"bù yī zú","population":"2,870,034"},{"name":"哈萨克族","pinyin":"hā sà kè zú","population":"1,462,588"},{"name":"门巴族","pinyin":"mén bā zú","population":"10,561"},{"name":"土族","pinyin":"tǔ zú","population":"289,565"},{"name":"朝鲜族","pinyin":"cháo xiǎn zú","population":"1,830,929"},{"name":"汉族","pinyin":"hàn zú","population":"1,220,844,520"},{"name":"蒙古族","pinyin":"měng gǔ zú","population":"5,981,840"},{"name":"佤族","pinyin":"wǎ zú","population":"429,709"},{"name":"达斡尔族","pinyin":"dá wò ěr zú","population":"131,992"},{"name":"赫哲族","pinyin":"hè zhé zú","population":"5,354"},{"name":"苗族","pinyin":"miáo zú","population":"9,426,007"},{"name":"维吾尔族","pinyin":"wéi wú ěr zú","population":"10,069,346"},{"name":"傣族","pinyin":"dǎi zú","population":"1,261,311"},{"name":"回族","pinyin":"huí zú","population":"10,586,087"},{"name":"仫佬族","pinyin":"mù lǎo zú","population":"216,257"},{"name":"乌孜别克族","pinyin":"wū zī bié kè zú","population":"10,569"},{"name":"德昂族","pinyin":"dé áng zú","population":"20,556"},{"name":"基诺族","pinyin":"jī nuò zú","population":"23,143"},{"name":"纳西族","pinyin":"nà xī zú","population":"326,295"},{"name":"锡伯族","pinyin":"xī bó zu","population":"190,481"},{"name":"东乡族","pinyin":"dōng xiāng zú","population":"621,500"},{"name":"京族","pinyin":"jīng zú","population":"28,199"},{"name":"怒族","pinyin":"nù zú","population":"37,523"},{"name":"瑶族","pinyin":"yáo zú","population":"2,796,003"},{"name":"侗族","pinyin":"dòng zú","population":"2,879,974"},{"name":"景颇族","pinyin":"jǐng pō zú","population":"147,828"},{"name":"普米族","pinyin":"pǔ mǐ zú","population":"42,861"},{"name":"彝族","pinyin":"yí zú","population":"8,714,393"},{"name":"独龙族","pinyin":"dú lóng zú","population":"6,930"},{"name":"柯尔克孜族","pinyin":"kē ěr kè zī zú","population":"186,708"},{"name":"羌族","pinyin":"qiāng zú","population":"309,576"},{"name":"裕固族","pinyin":"yù gù zú","population":"14,378"},{"name":"俄罗斯族","pinyin":"é luó sī zú","population":"15,393"},{"name":"拉祜族","pinyin":"lā hù zú","population":"485,966"},{"name":"撒拉族","pinyin":"sā lā zú","population":"130,607"},{"name":"藏族","pinyin":"zàng zú","population":"6,282,187"},{"name":"鄂伦春族","pinyin":"è lún chūn zu","population":"8,659"},{"name":"黎族","pinyin":"lí zú","population":"1,463,064"},{"name":"畲族","pinyin":"shē zú","population":"708,651"},{"name":"壮族","pinyin":"zhuàng zú","population":"16,926,381"}] -------------------------------------------------------------------------------- /src/Command/ShowHolidayCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Holiday\HolidayInterface; 17 | use Symfony\Component\Console\Input\InputInterface; 18 | use Symfony\Component\Console\Input\InputOption; 19 | use Symfony\Component\Console\Output\OutputInterface; 20 | use Symfony\Component\Console\Question\ChoiceQuestion; 21 | use Symfony\Component\Console\Style\SymfonyStyle; 22 | 23 | class ShowHolidayCommand extends DashboardCommand 24 | { 25 | protected static $types = [ 26 | HolidayInterface::TYPE_TRADITIONAL => '传统节日', 27 | HolidayInterface::TYPE_INTERNATIONAL => '国际节日', 28 | HolidayInterface::TYPE_SOLAR_TERM => '24节气', 29 | ]; 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function configure() 35 | { 36 | $this->setName('view:holiday'); 37 | $this->addOption('type', 't', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, '按照类型筛选'); 38 | $this->addOption('all', 'a', InputOption::VALUE_NONE, '展现全部数据'); 39 | $this->setDescription('展示节假日信息'); 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function execute(InputInterface $input, OutputInterface $output) 46 | { 47 | $holidayService = $this->getChina()->getHoliday(); 48 | $style = new SymfonyStyle($input, $output); 49 | $headers = ['名称', '类型', '日期']; 50 | 51 | if ($input->getOption('all')) { 52 | $holidays = $holidayService->findAll()->toArray(); 53 | } else { 54 | $types = $input->getOption('type'); 55 | if (empty($types)) { 56 | $question = new ChoiceQuestion('请选择节日类型', static::$types, HolidayInterface::TYPE_TRADITIONAL); 57 | $question->setMultiselect(true); 58 | $helper = $this->getHelper('question'); 59 | $types = $helper->ask($input, $output, $question); 60 | } else { 61 | $this->checkTypes($types); 62 | } 63 | $holidays = []; 64 | foreach ($types as $type) { 65 | $holidays = array_merge($holidays, $holidayService->findHolidaysByType($type)->toArray()); 66 | } 67 | } 68 | $rows = array_map(function(HolidayInterface $holiday){ 69 | return [ 70 | "{$holiday->getName()}", 71 | static::$types[$holiday->getType()], 72 | $holiday->getDate(), 73 | ]; 74 | }, $holidays); 75 | $style->table($headers, $rows); 76 | return 0; 77 | } 78 | 79 | protected function checkTypes($types) 80 | { 81 | foreach ($types as $type) { 82 | if (!isset(static::$types[$type])) { 83 | throw new \InvalidArgumentException(sprintf('类型 "%s" 不支持,请从(%s)做出选择', 84 | $type, 85 | implode(', ', array_keys(static::$types)) 86 | )); 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/Command/CrawlNationalityCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Nationality\Nationality; 17 | use Symfony\Component\Console\Input\InputInterface; 18 | use Symfony\Component\Console\Output\OutputInterface; 19 | use Symfony\Component\Console\Style\SymfonyStyle; 20 | use Symfony\Component\DomCrawler\Crawler; 21 | 22 | class CrawlNationalityCommand extends CrawlCommand 23 | { 24 | /** 25 | * @var string 26 | */ 27 | const URL = 'https://baike.baidu.com/item/56个民族/383735'; 28 | 29 | /** 30 | * {@inheritdoc} 31 | */ 32 | public function configure() 33 | { 34 | $this->setName('crawl:nationality'); 35 | $this->setDescription('从百度百科采集民族信息'); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function execute(InputInterface $input, OutputInterface $output) 42 | { 43 | $style = new SymfonyStyle($input, $output); 44 | 45 | $outputFile = $this->resourceDir.'/nationalities.json'; 46 | 47 | $crawler = $this->getClient()->request('GET', static::URL); 48 | 49 | $tables = $crawler->filter('table[log-set-param="table_view"]'); 50 | $nationalities = $this->extractPinyinData($tables->eq(1)); 51 | $populations = $this->extractPopulationData($tables->eq(2)); 52 | 53 | $nationalities = $this->mergeData($nationalities,$populations); 54 | $this->filesystem->dumpFile($outputFile, \json_encode($nationalities, JSON_UNESCAPED_UNICODE)); 55 | 56 | $style->writeln(sprintf('Crawl completed, please check the file at "%s"', realpath($outputFile))); 57 | return 0; 58 | } 59 | 60 | protected function extractPinyinData(Crawler $crawler) 61 | { 62 | $nationalities = $crawler->filter('tr')->each(function(Crawler $itemNode){ 63 | $data = []; 64 | $itemNode->filter('td')->each(function(Crawler $tdNode, $index) use (&$data){ 65 | if ($index % 2 === 0) { 66 | $data[$index] = [ 67 | 'name' => trim($tdNode->text()), 68 | 'pinyin' => false, 69 | ]; 70 | } else { 71 | $data[$index - 1]['pinyin'] = trim($tdNode->text()); 72 | } 73 | }); 74 | return $data; 75 | }); 76 | 77 | return call_user_func_array('array_merge', $nationalities); 78 | } 79 | 80 | protected function extractPopulationData(Crawler $crawler): array 81 | { 82 | $data = []; 83 | $crawler->filter('tr')->each(function(Crawler $itemNode) use (&$data){ 84 | $tds = $itemNode->filter('td'); 85 | if (count($tds) > 0) { 86 | $name = trim($tds->first()->text()); 87 | $data[$name] = trim($tds->eq(1)->text()); 88 | } 89 | }); 90 | 91 | return $data; 92 | } 93 | 94 | protected function mergeData($nationalityInfos, $populations): array 95 | { 96 | $nationalities = []; 97 | foreach ($nationalityInfos as $nationalityInfo) { 98 | $nationalities[] = new Nationality($nationalityInfo['name'], 99 | $nationalityInfo['pinyin'], 100 | $populations[$nationalityInfo['name']] ?? 0 101 | ); 102 | } 103 | 104 | return $nationalities; 105 | } 106 | } -------------------------------------------------------------------------------- /src/China.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China; 15 | 16 | use China\Common\ResourceFile; 17 | use China\Holiday\HolidayService; 18 | use China\Holiday\HolidayServiceInterface; 19 | use China\Nationality\NationalityService; 20 | use China\Nationality\NationalityServiceInterface; 21 | use China\Region\RegionService; 22 | use China\Region\RegionServiceInterface; 23 | use Slince\Di\Container; 24 | 25 | final class China 26 | { 27 | /** 28 | * @var string 29 | */ 30 | protected $name = '中国'; 31 | 32 | /** 33 | * @var string 34 | */ 35 | protected $officialName = '中华人民共和国'; 36 | 37 | /** 38 | * @var string 39 | */ 40 | protected $isoCode = 'CN'; 41 | 42 | /** 43 | * @var array 44 | */ 45 | protected $languages = ['zh_CN', 'zh_TW']; 46 | 47 | /** 48 | * @var string 49 | */ 50 | protected $resourceDir; 51 | 52 | protected $container; 53 | 54 | public function __construct(string $resourceDir = __DIR__.'/../resources/') 55 | { 56 | $this->container = new Container(); 57 | $this->resourceDir = $resourceDir; 58 | $this->registerParameters(); 59 | $this->registerService(); 60 | } 61 | 62 | protected function registerParameters() 63 | { 64 | $this->container->setParameters([ 65 | 'resource.file.holidays' => $this->resourceDir.'holidays.json', 66 | 'resource.file.nationalities' => $this->resourceDir.'nationalities.json', 67 | 'resource.file.regions' => $this->resourceDir.'regions/regions.json', 68 | ]); 69 | } 70 | 71 | protected function registerService() 72 | { 73 | $this->container->register('holiday', function(Container $container){ 74 | return new HolidayService(new ResourceFile($container->getParameter('resource.file.holidays'))); 75 | }); 76 | $this->container->register('nationality', function(Container $container){ 77 | return new NationalityService(new ResourceFile($container->getParameter('resource.file.nationalities'))); 78 | }); 79 | $this->container->register('region', function(Container $container){ 80 | return new RegionService(new ResourceFile($container->getParameter('resource.file.regions'))); 81 | }); 82 | } 83 | 84 | /** 85 | * 获取名称. 86 | * 87 | * @return string 88 | */ 89 | public function getName(): string 90 | { 91 | return $this->name; 92 | } 93 | 94 | /** 95 | * 获取官方名称. 96 | * 97 | * @return string 98 | */ 99 | public function getOfficialName(): string 100 | { 101 | return $this->officialName; 102 | } 103 | 104 | /** 105 | * 获取ISO3166两位代码 106 | * 107 | * @return string 108 | */ 109 | public function getIsoCode(): string 110 | { 111 | return $this->isoCode; 112 | } 113 | 114 | /** 115 | * 获取语言 116 | * 117 | * @return array 118 | */ 119 | public function getLanguages(): array 120 | { 121 | return $this->languages; 122 | } 123 | 124 | /** 125 | * 获取Holiday服务 126 | * 127 | * @return HolidayServiceInterface 128 | */ 129 | public function getHoliday(): HolidayServiceInterface 130 | { 131 | return $this->container->get('holiday'); 132 | } 133 | 134 | /** 135 | * 获取Nationality服务 136 | * 137 | * @return NationalityServiceInterface 138 | */ 139 | public function getNationality(): NationalityServiceInterface 140 | { 141 | return $this->container->get('nationality'); 142 | } 143 | 144 | /** 145 | * 获取Region服务 146 | * 147 | * @return RegionServiceInterface 148 | */ 149 | public function getRegion(): RegionServiceInterface 150 | { 151 | return $this->container->get('region'); 152 | } 153 | } -------------------------------------------------------------------------------- /src/Region/Location/Address.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Region\Location; 15 | 16 | use China\Region\RegionCollection; 17 | use Doctrine\Common\Collections\Collection; 18 | 19 | abstract class Address implements AddressInterface 20 | { 21 | /** 22 | * @var int 23 | */ 24 | protected $code; 25 | 26 | /** 27 | * @var string 28 | */ 29 | protected $name; 30 | 31 | /** 32 | * @var AddressInterface 33 | */ 34 | protected $parent; 35 | 36 | /** 37 | * 子地区. 38 | * 39 | * @var AddressInterface[]|Collection 40 | */ 41 | protected $children; 42 | 43 | public function __construct(int $code, string $name, AddressInterface $parent = null, $children = []) 44 | { 45 | $this->code = $code; 46 | $this->name = $name; 47 | $this->parent = $parent; 48 | $this->children = $children; 49 | } 50 | 51 | public function __toString(): string 52 | { 53 | return $this->getName(); 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function getName(): string 60 | { 61 | return $this->name; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function getCode(): int 68 | { 69 | return $this->code; 70 | } 71 | 72 | /** 73 | * {@inheritdoc} 74 | */ 75 | public function getParent(): ?AddressInterface 76 | { 77 | return $this->parent; 78 | } 79 | 80 | /** 81 | * {@inheritdoc} 82 | */ 83 | public function setChildren($children) 84 | { 85 | $this->children = $children; 86 | } 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | public function getChildren(): iterable 92 | { 93 | return $this->children; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function jsonSerialize() 100 | { 101 | return [ 102 | 'code' => $this->code, 103 | 'name' => $this->name, 104 | 'type' => static::getType(), 105 | 'children' => $this->getChildren(), 106 | ]; 107 | } 108 | 109 | /** 110 | * 获取当前类类型. 111 | * @return string 112 | */ 113 | public static function getType(): string 114 | { 115 | return ''; 116 | } 117 | 118 | /** 119 | * 创建地区对象 120 | * 121 | * @param array $data 122 | * @param AddressInterface|null $parent 123 | * 124 | * @return AddressInterface 125 | * @throws \InvalidArgumentException 126 | */ 127 | public static function createFromArray(array $data, AddressInterface $parent = null) 128 | { 129 | if (!isset($data['type'])) { 130 | throw new \InvalidArgumentException('Missing parameter "type"'); 131 | } 132 | $address = null; 133 | $code = intval($data['code']); 134 | switch ($data['type']) { 135 | case AddressInterface::TYPE_PROVINCE: 136 | $address = new Province($code, $data['name'], $parent); 137 | break; 138 | case AddressInterface::TYPE_CITY: 139 | $address = new City($code, $data['name'], $parent); 140 | break; 141 | case AddressInterface::TYPE_DISTRICT: 142 | $address = new District($code, $data['name'], $parent); 143 | break; 144 | } 145 | if (!$address) { 146 | throw new \InvalidArgumentException(sprintf('Bad parameter "type" with "%s"', $data['type'])); 147 | } 148 | //子地区 149 | if (!empty($data['children'])) { 150 | $children = array_map(function($regionData) use ($address){ 151 | return static::createFromArray($regionData, $address); 152 | }, $data['children']); 153 | $address->setChildren(new RegionCollection($children)); 154 | } 155 | return $address; 156 | } 157 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 中国 3 |

4 | 5 | 我国的资料库查询库;本库主要包含民族、节日、地址库信息的查询;部分数据来源采集自百科以及国家统计局;欢迎收藏欢迎贡献代码; 6 | 7 | [![Build Status](https://img.shields.io/github/workflow/status/slince/China/test?style=flat-square)](https://github.com/slince/China/actions) 8 | [![Coverage Status](https://img.shields.io/codecov/c/github/slince/China/master?style=flat-square)](https://codecov.io/github/slince/China) 9 | [![Latest Stable Version](https://img.shields.io/packagist/v/slince/china.svg?style=flat-square&label=stable)](https://packagist.org/packages/slince/china) 10 | [![Scrutinizer](https://img.shields.io/scrutinizer/g/slince/China.svg?style=flat-square)](https://scrutinizer-ci.com/g/slince/China/?branch=master) 11 | 12 | 13 | ## Installation 14 | 15 | 执行下面命令安装 16 | 17 | ```bash 18 | > composer require slince/china 19 | ``` 20 | 21 | ## Document 22 | 23 | 你可以在这里发现[文档](./doc); 24 | 25 | ## Basic Usage 26 | 27 | ### 节日查询 28 | 29 | ```php 30 | 31 | $china = new China(); 32 | 33 | $holidayService = $chna->getHoliday(); 34 | 35 | print_r($holidayService->findAll()); 36 | 37 | ``` 38 | 按名称查找节日 39 | 40 | ```php 41 | $yuandan = $holidayService->find('元旦节'): 42 | 43 | echo $yuandan->getDate(); //输出 1月1日 44 | ``` 45 | 46 | ### 民族查询 47 | 48 | ```php 49 | 50 | $china = new China(); 51 | 52 | $nationalityService = $chna->getNationality(); 53 | 54 | print_r($nationalityService->findAll()); 55 | 56 | ``` 57 | 58 | 按名称查找 59 | 60 | ```php 61 | $hezhe = $nationalityService->find('赫哲族'): 62 | 63 | echo $hezhe->getPinyin(); //输出“hè zhé zú” 64 | echo $hezhe->getPopulation(); //输出人口 65 | ``` 66 | 67 | 68 | ### 地址库查询 69 | 70 | 71 | ```php 72 | 73 | $china = new China(); 74 | 75 | $regionService = $china->getRegion(); 76 | 77 | print_r($regionService->getProvinces()); //获取树形省市县地区结构 78 | ``` 79 | 80 | 按名称查找地区 81 | 82 | ```php 83 | $bengbushi = $regionService->findByName('蚌埠市'); 84 | echo $bengbushi->getParent()->getName(); //输出蚌埠市所属省, 安徽省 85 | ``` 86 | 87 | 查询身份证所在地 88 | 89 | ```php 90 | $huaiyuan = $regionService->findByIdCard('340321199212026972'); 91 | ``` 92 | 93 | 地区链功能 94 | 95 | 通过`AddressInterface`的`getParent()`和`getChildren()`方法你可以自由地查找当前地区的上级或者下级地区,下级地区集合是`RegionCollectionInterfaec` 96 | 实例; 97 | 98 | ```php 99 | $yuhuiqu = $bengbushi->getChildren()->findByName('禹会区'); //蚌埠市下属禹会区 100 | $yuhuiqu->getParent()->getChildren()->findByName('淮上区'); //禹会区同级淮上区 101 | ``` 102 | Json Serialize 103 | 104 | ```php 105 | $bengbushi = $regionService->findByName('蚌埠市'); 106 | echo json_encode($bengbushi); 107 | ``` 108 | 上述代码会输出蚌埠市及其下属地区的完整的json树形结构;如果你不需要的话下级地区的话请将children设置为空 109 | 110 | ```php 111 | $bengbushi = $regionService->findByName('蚌埠市'); 112 | $bengbushi = clone $bengbushi; //此处需要克隆对象,如果直接修改会破坏原有的地区树结构 113 | $bengbushi->setChildren([]); 114 | echo json_encode($bengbushi); 115 | ``` 116 | 117 | > 建议:在电商系统中城市县三级地区联动的数据可以使用此方法获取,避免将数据导入数据库再从数据库获取; 118 | 119 | 120 | ## 命令行工具 121 | 122 | 执行下面命令查看支持的所有命令 123 | 124 | ```bash 125 | $ china list 126 | ``` 127 | 输出信息: 128 | ``` 129 | Available commands: 130 | help Displays help for a command 131 | list Lists commands 132 | crawl 133 | crawl:holiday 采集节假日数据 134 | crawl:nationality 从百度百科采集民族信息 135 | crawl:region 从国家统计局采集地区信息 136 | view 137 | view:holiday 展示节假日信息 138 | view:nationality 展示民族数据信息 139 | view:region 展示我国省市县信息 140 | ``` 141 | 142 | 例: 展示我国所有的省、直辖市、自治区以及特别行政区: 143 | 144 | ```bash 145 | $ china dashboard:region 146 | ``` 147 | 上述命令会输出以下结果: 148 | 149 | ``` 150 | -------- ------------------ ------ 151 | Code 名称 类型 152 | -------- ------------------ ------ 153 | 110000 北京市 省 154 | 120000 天津市 省 155 | 130000 河北省 省 156 | 140000 山西省 省 157 | 150000 内蒙古自治区 省 158 | 210000 辽宁省 省 159 | 220000 吉林省 省 160 | 230000 黑龙江省 省 161 | 310000 上海市 省 162 | 320000 江苏省 省 163 | 330000 浙江省 省 164 | 340000 安徽省 省 165 | 350000 福建省 省 166 | 360000 江西省 省 167 | 370000 山东省 省 168 | 410000 河南省 省 169 | 420000 湖北省 省 170 | 430000 湖南省 省 171 | 440000 广东省 省 172 | 450000 广西壮族自治区 省 173 | 460000 海南省 省 174 | 500000 重庆市 省 175 | 510000 四川省 省 176 | 520000 贵州省 省 177 | 530000 云南省 省 178 | 540000 西藏自治区 省 179 | 610000 陕西省 省 180 | 620000 甘肃省 省 181 | 630000 青海省 省 182 | 640000 宁夏回族自治区 省 183 | 650000 新疆维吾尔自治区 省 184 | 710000 台湾省 省 185 | 810000 香港特别行政区 省 186 | 820000 澳门特别行政区 省 187 | -------- ------------------ ------ 188 | ``` 189 | ... 190 | 191 | ## License 192 | 193 | The MIT license. See [MIT](https://opensource.org/licenses/MIT) 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /src/Command/CrawlRegionCommand.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * For the full copyright and license information, please view the LICENSE 11 | * file that was distributed with this source code. 12 | */ 13 | 14 | namespace China\Command; 15 | 16 | use China\Common\Utils; 17 | use China\Region\Location\AddressInterface; 18 | use China\Region\Location\District; 19 | use China\Region\Location\City; 20 | use China\Region\Location\Province; 21 | use Symfony\Component\Console\Input\InputInterface; 22 | use Symfony\Component\Console\Output\OutputInterface; 23 | use Symfony\Component\Console\Style\SymfonyStyle; 24 | use Symfony\Component\DomCrawler\Crawler; 25 | 26 | class CrawlRegionCommand extends CrawlCommand 27 | { 28 | /** 29 | * 资源地址 30 | * 31 | * @var string 32 | */ 33 | const URL = 'http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html'; 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function configure() 39 | { 40 | $this->setName('crawl:region'); 41 | $this->setDescription('从国家统计局采集地区信息'); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function execute(InputInterface $input, OutputInterface $output) 48 | { 49 | $style = new SymfonyStyle($input, $output); 50 | 51 | $outputFile = $this->resourceDir.'/regions/regions.json'; 52 | 53 | $crawler = $this->getClient()->request('GET', static::URL); 54 | 55 | $regions = $crawler->filter('p.MsoNormal')->each(function(Crawler $node) use (&$provinces, &$cities, &$areas){ 56 | $code = $node->filter('span[lang="EN-US"]')->text(); 57 | $name = $node->filter('span[style]')->last()->text(); 58 | 59 | return [ 60 | 'code' => preg_replace('/[^\d]/', '', $code), 61 | 'name' => Utils::clearBlankCharacters($name), 62 | ]; 63 | }); 64 | //归类数据 65 | list($provinces, $cities, $districts) = $this->organizeRegions($regions); 66 | //构建树形结构 67 | $root = new Province(0, ""); 68 | $root->shortCode = 0; 69 | $this->buildRegionsTree(array_merge($provinces, $cities, $districts), $root); 70 | 71 | $this->filesystem->dumpFile($this->resourceDir.'/regions/provinces.json', \json_encode($this->extractAddressesWithoutChildren($provinces), JSON_UNESCAPED_UNICODE)); 72 | $this->filesystem->dumpFile($this->resourceDir.'/regions/cities.json', \json_encode($this->extractAddressesWithoutChildren($cities), JSON_UNESCAPED_UNICODE)); 73 | $this->filesystem->dumpFile($this->resourceDir.'/regions/districts.json', \json_encode($this->extractAddressesWithoutChildren($districts), JSON_UNESCAPED_UNICODE)); 74 | $this->filesystem->dumpFile($outputFile, \json_encode($root->getChildren(), JSON_UNESCAPED_UNICODE)); 75 | 76 | $style->writeln(sprintf('Crawl completed, please check the file at "%s"', realpath($outputFile))); 77 | return 0; 78 | } 79 | 80 | /** 81 | * 提取省份数据,去除子地区数据. 82 | * 83 | * @param AddressInterface[] $addresses 84 | * 85 | * @return AddressInterface[] 86 | */ 87 | protected function extractAddressesWithoutChildren(array $addresses): array 88 | { 89 | return array_map(function(AddressInterface $address){ 90 | $address = clone $address; 91 | $address->setChildren([]); 92 | 93 | return $address; 94 | }, $addresses); 95 | } 96 | 97 | /** 98 | * 分拣数据. 99 | * 100 | * @param array $regions 101 | * 102 | * @return array 103 | */ 104 | protected function organizeRegions(array $regions): array 105 | { 106 | $provinces = $cities = $districts = []; 107 | foreach ($regions as $regionData) { 108 | if (substr($regionData['code'], 2) === '0000') { 109 | $province = new Province($regionData['code'], $regionData['name']); 110 | $province->parentCode = 0; 111 | $province->shortCode = substr($regionData['code'], 0, 2); 112 | $provinces[] = $province; 113 | } elseif (substr($regionData['code'], 4) === '00') { 114 | $city = new City($regionData['code'], $regionData['name']); 115 | $city->parentCode = substr($regionData['code'], 0, 2); 116 | $city->shortCode = substr($regionData['code'], 0, 4); 117 | $cities[] = $city; 118 | } else { 119 | $area = new District($regionData['code'], $regionData['name']); 120 | $area->parentCode = substr($regionData['code'], 0, 4); 121 | $area->shortCode = $regionData['code']; 122 | $districts[] = $area; 123 | } 124 | } 125 | 126 | return [ 127 | $provinces, 128 | $cities, 129 | $districts, 130 | ]; 131 | } 132 | 133 | protected function buildRegionsTree($addresses, AddressInterface $address) 134 | { 135 | $children = []; 136 | foreach ($addresses as $index => $_address) { 137 | if ($_address->parentCode == $address->shortCode) { 138 | unset($addresses[$index]); 139 | $this->buildRegionsTree($addresses, $_address); 140 | $children[] = $_address; 141 | } 142 | } 143 | $address->setChildren($children); 144 | } 145 | } -------------------------------------------------------------------------------- /resources/holidays.json: -------------------------------------------------------------------------------- 1 | [{"name":"元旦","type":"traditional","date":"01月01日"},{"name":"黑人日","type":"international","date":"01月03日"},{"name":"腊八节","type":"traditional","date":"01月05日"},{"name":"大寒","type":"solar_term","date":"01月20日"},{"name":"小年","type":"traditional","date":"01月20日"},{"name":"国际海关日","type":"international","date":"01月26日"},{"name":"除夕","type":"traditional","date":"01月27日"},{"name":"春节","type":"traditional","date":"01月28日"},{"name":"国际麻风节","type":"international","date":"01月31日"},{"name":"世界湿地日","type":"international","date":"02月02日"},{"name":"立春","type":"solar_term","date":"02月03日"},{"name":"世界抗癌症日","type":"international","date":"02月04日"},{"name":"国际声援南非日","type":"international","date":"02月7日"},{"name":"国际气象节","type":"international","date":"02月10日"},{"name":"元宵节","type":"traditional","date":"02月11日"},{"name":"情人节","type":"international","date":"02月14日"},{"name":"雨水","type":"solar_term","date":"02月18日"},{"name":"国际母语日","type":"international","date":"02月21日"},{"name":"第三世界青年日","type":"international","date":"02月24日"},{"name":"龙抬头","type":"traditional","date":"02月27日"},{"name":"国际罕见病日","type":"international","date":"02月29日"},{"name":"国际海豹日","type":"international","date":"03月01日"},{"name":"国际爱耳日","type":"international","date":"03月03日"},{"name":"惊蛰","type":"solar_term","date":"03月06日"},{"name":"学雷锋日","type":"traditional","date":"03月05日"},{"name":"三八妇女节","type":"traditional","date":"03月08日"},{"name":"植树节","type":"international","date":"03月12日"},{"name":"白色情人节","type":"international","date":"03月14日"},{"name":"消费者权益日","type":"international","date":"03月15日"},{"name":"国际航海日","type":"international","date":"03月17日"},{"name":"世界无肉日","type":"international","date":"03月20日"},{"name":"春分","type":"solar_term","date":"03月20日"},{"name":"世界睡眠日","type":"international","date":"03月21日"},{"name":"世界水日","type":"international","date":"03月22日"},{"name":"世界气象日","type":"international","date":"03月23日"},{"name":"愚人节","type":"international","date":"04月01日"},{"name":"国际儿童图书日","type":"international","date":"04月02日"},{"name":"寒食节","type":"traditional","date":"04月03日"},{"name":"清明节","type":"traditional","date":"04月04日"},{"name":"世界卫生日","type":"international","date":"04月07日"},{"name":"世界帕金森日","type":"international","date":"04月11日"},{"name":"复活节","type":"international","date":"04月16日"},{"name":"谷雨","type":"solar_term","date":"04月20日"},{"name":"世界地球日","type":"international","date":"04月22日"},{"name":"世界读书日","type":"international","date":"04月23日"},{"name":"世界儿童日","type":"international","date":"04月24日"},{"name":"知识产权日","type":"international","date":"04月26日"},{"name":"佛诞","type":"traditional","date":"04月29日"},{"name":"全国交通安全反思日","type":"international","date":"04月30日"},{"name":"国际劳动节","type":"international","date":"05月01日"},{"name":"五四青年节","type":"traditional","date":"05月04日"},{"name":"立夏","type":"solar_term","date":"05月05日"},{"name":"世界红十字日","type":"international","date":"05月08日"},{"name":"世界微笑日","type":"international","date":"05月08日"},{"name":"母亲节","type":"international","date":"05月14日"},{"name":"国际护士节","type":"international","date":"05月12日"},{"name":"国际家庭日","type":"international","date":"05月15日"},{"name":"全国助残日","type":"international","date":"05月15日"},{"name":"世界电信日","type":"international","date":"05月17日"},{"name":"国际博物馆日","type":"international","date":"05月18日"},{"name":"小满","type":"solar_term","date":"05月21日"},{"name":"五卅运动纪念","type":"traditional","date":"05月30日"},{"name":"端午节","type":"traditional","date":"05月30日"},{"name":"世界无烟日","type":"international","date":"05月31日"},{"name":"六一儿童节","type":"international","date":"06月01日"},{"name":"世界环境日","type":"international","date":"06月05日"},{"name":"芒种","type":"solar_term","date":"06月05日"},{"name":"全国爱眼日","type":"international","date":"06月06日"},{"name":"中国人口日","type":"traditional","date":"06月11日"},{"name":"父亲节","type":"international","date":"06月18日"},{"name":"世界难民日","type":"international","date":"06月20日"},{"name":"夏至","type":"solar_term","date":"06月21日"},{"name":"中国儿童慈善活动日","type":"traditional","date":"06月22日"},{"name":"国际奥林匹克日","type":"international","date":"06月23日"},{"name":"全国土地日","type":"international","date":"06月25日"},{"name":"国际禁毒日","type":"international","date":"06月26日"},{"name":"世界青年联欢节","type":"international","date":"06月30日"},{"name":"中国共产党诞生日","type":"traditional","date":"07月01日"},{"name":"香港回归日","type":"traditional","date":"07月01日"},{"name":"国际体育记者日","type":"international","date":"07月02日"},{"name":"国际接吻日","type":"international","date":"07月06日"},{"name":"小暑","type":"solar_term","date":"07月07日"},{"name":"世界人口日","type":"international","date":"07月11日"},{"name":"大暑","type":"solar_term","date":"07月22日"},{"name":"非洲妇女日","type":"international","date":"07月30日"},{"name":"八一建军节","type":"traditional","date":"08月01日"},{"name":"立秋","type":"solar_term","date":"08月07日"},{"name":"日本投降日","type":"international","date":"08月15日"},{"name":"处暑","type":"solar_term","date":"08月23日"},{"name":"七夕","type":"traditional","date":"08月28日"},{"name":"抗日战争胜利纪念日","type":"traditional","date":"09月03日"},{"name":"中元节","type":"traditional","date":"09月05日"},{"name":"白露","type":"solar_term","date":"09月07日"},{"name":"国际扫盲日","type":"international","date":"09月08日"},{"name":"教师节","type":"international","date":"09月10日"},{"name":"国际臭氧层保护日","type":"international","date":"09月16日"},{"name":"“九一八”事变纪念日","type":"traditional","date":"09月18日"},{"name":"国际和平日","type":"international","date":"09月21日"},{"name":"国际爱牙日","type":"international","date":"09月20日"},{"name":"世界无车日","type":"international","date":"09月22日"},{"name":"秋分","type":"solar_term","date":"09月23日"},{"name":"国际聋人节","type":"international","date":"09月24日"},{"name":"世界旅游日","type":"international","date":"09月27日"},{"name":"国庆节","type":"traditional","date":"10月01日"},{"name":"世界动物日","type":"international","date":"10月04日"},{"name":"中秋节","type":"traditional","date":"10月04日"},{"name":"寒露","type":"solar_term","date":"10月08日"},{"name":"世界邮政日","type":"international","date":"10月09日"},{"name":"辛亥革命纪念日","type":"traditional","date":"10月10日"},{"name":"中国少年先锋队诞辰日","type":"traditional","date":"10月13日"},{"name":"世界标准日","type":"international","date":"10月14日"},{"name":"国际盲人节","type":"international","date":"10月15日"},{"name":"世界粮食日","type":"international","date":"10月16日"},{"name":"国际消除贫困日","type":"international","date":"10月17日"},{"name":"世界传统医药日","type":"international","date":"10月22日"},{"name":"霜降","type":"solar_term","date":"10月23日"},{"name":"联合国日","type":"international","date":"10月24日"},{"name":"重阳","type":"traditional","date":"10月28日"},{"name":"世界勤俭日","type":"international","date":"10月31日"},{"name":"万圣节","type":"international","date":"10月31日"},{"name":"立冬","type":"solar_term","date":"11月07日"},{"name":"中国记者日","type":"traditional","date":"11月08日"},{"name":"世界青年节","type":"international","date":"11月10日"},{"name":"光棍节","type":"international","date":"11月11日"},{"name":"世界糖尿病日","type":"international","date":"11月14日"},{"name":"国际大学生节","type":"international","date":"11月17日"},{"name":"世界问候日","type":"international","date":"11月21日"},{"name":"小雪","type":"solar_term","date":"11月22日"},{"name":"感恩节","type":"international","date":"11月23日"},{"name":"世界艾滋病日","type":"international","date":"12月01日"},{"name":"国际残疾人日","type":"international","date":"12月03日"},{"name":"大雪","type":"solar_term","date":"12月07日"},{"name":"“一二九”运动纪念日","type":"traditional","date":"12月09日"},{"name":"世界足球日","type":"international","date":"12月09日"},{"name":"世界人权日","type":"international","date":"12月10日"},{"name":"西安事变纪念日","type":"traditional","date":"12月12日"},{"name":"南京大屠杀","type":"traditional","date":"12月13日"},{"name":"澳门回归日","type":"traditional","date":"12月20日"},{"name":"国际篮球日","type":"international","date":"12月21日"},{"name":"冬至","type":"solar_term","date":"12月22日"},{"name":"平安夜","type":"international","date":"12月24日"},{"name":"圣诞节","type":"international","date":"12月25日"}] -------------------------------------------------------------------------------- /resources/regions/cities.json: -------------------------------------------------------------------------------- 1 | [{"code":"110100","name":"市辖区","type":"city","children":[]},{"code":"120100","name":"市辖区","type":"city","children":[]},{"code":"130100","name":"石家庄市","type":"city","children":[]},{"code":"130200","name":"唐山市","type":"city","children":[]},{"code":"130300","name":"秦皇岛市","type":"city","children":[]},{"code":"130400","name":"邯郸市","type":"city","children":[]},{"code":"130500","name":"邢台市","type":"city","children":[]},{"code":"130600","name":"保定市","type":"city","children":[]},{"code":"130700","name":"张家口市","type":"city","children":[]},{"code":"130800","name":"承德市","type":"city","children":[]},{"code":"130900","name":"沧州市","type":"city","children":[]},{"code":"131000","name":"廊坊市","type":"city","children":[]},{"code":"131100","name":"衡水市","type":"city","children":[]},{"code":"139000","name":"省直辖县级行政区划","type":"city","children":[]},{"code":"140100","name":"太原市","type":"city","children":[]},{"code":"140200","name":"大同市","type":"city","children":[]},{"code":"140300","name":"阳泉市","type":"city","children":[]},{"code":"140400","name":"长治市","type":"city","children":[]},{"code":"140500","name":"晋城市","type":"city","children":[]},{"code":"140600","name":"朔州市","type":"city","children":[]},{"code":"140700","name":"晋中市","type":"city","children":[]},{"code":"140800","name":"运城市","type":"city","children":[]},{"code":"140900","name":"忻州市","type":"city","children":[]},{"code":"141000","name":"临汾市","type":"city","children":[]},{"code":"141100","name":"吕梁市","type":"city","children":[]},{"code":"150100","name":"呼和浩特市","type":"city","children":[]},{"code":"150200","name":"包头市","type":"city","children":[]},{"code":"150300","name":"乌海市","type":"city","children":[]},{"code":"150400","name":"赤峰市","type":"city","children":[]},{"code":"150500","name":"通辽市","type":"city","children":[]},{"code":"150600","name":"鄂尔多斯市","type":"city","children":[]},{"code":"150700","name":"呼伦贝尔市","type":"city","children":[]},{"code":"150800","name":"巴彦淖尔市","type":"city","children":[]},{"code":"150900","name":"乌兰察布市","type":"city","children":[]},{"code":"152200","name":"兴安盟","type":"city","children":[]},{"code":"152500","name":"锡林郭勒盟","type":"city","children":[]},{"code":"152900","name":"阿拉善盟","type":"city","children":[]},{"code":"210100","name":"沈阳市","type":"city","children":[]},{"code":"210200","name":"大连市","type":"city","children":[]},{"code":"210300","name":"鞍山市","type":"city","children":[]},{"code":"210400","name":"抚顺市","type":"city","children":[]},{"code":"210500","name":"本溪市","type":"city","children":[]},{"code":"210600","name":"丹东市","type":"city","children":[]},{"code":"210700","name":"锦州市","type":"city","children":[]},{"code":"210800","name":"营口市","type":"city","children":[]},{"code":"210900","name":"阜新市","type":"city","children":[]},{"code":"211000","name":"辽阳市","type":"city","children":[]},{"code":"211100","name":"盘锦市","type":"city","children":[]},{"code":"211200","name":"铁岭市","type":"city","children":[]},{"code":"211300","name":"朝阳市","type":"city","children":[]},{"code":"211400","name":"葫芦岛市","type":"city","children":[]},{"code":"220100","name":"长春市","type":"city","children":[]},{"code":"220200","name":"吉林市","type":"city","children":[]},{"code":"220300","name":"四平市","type":"city","children":[]},{"code":"220400","name":"辽源市","type":"city","children":[]},{"code":"220500","name":"通化市","type":"city","children":[]},{"code":"220600","name":"白山市","type":"city","children":[]},{"code":"220700","name":"松原市","type":"city","children":[]},{"code":"220800","name":"白城市","type":"city","children":[]},{"code":"222400","name":"延边朝鲜族自治州","type":"city","children":[]},{"code":"230100","name":"哈尔滨市","type":"city","children":[]},{"code":"230200","name":"齐齐哈尔市","type":"city","children":[]},{"code":"230300","name":"鸡西市","type":"city","children":[]},{"code":"230400","name":"鹤岗市","type":"city","children":[]},{"code":"230500","name":"双鸭山市","type":"city","children":[]},{"code":"230600","name":"大庆市","type":"city","children":[]},{"code":"230700","name":"伊春市","type":"city","children":[]},{"code":"230800","name":"佳木斯市","type":"city","children":[]},{"code":"230900","name":"七台河市","type":"city","children":[]},{"code":"231000","name":"牡丹江市","type":"city","children":[]},{"code":"231100","name":"黑河市","type":"city","children":[]},{"code":"231200","name":"绥化市","type":"city","children":[]},{"code":"232700","name":"大兴安岭地区","type":"city","children":[]},{"code":"310100","name":"市辖区","type":"city","children":[]},{"code":"320100","name":"南京市","type":"city","children":[]},{"code":"320200","name":"无锡市","type":"city","children":[]},{"code":"320300","name":"徐州市","type":"city","children":[]},{"code":"320400","name":"常州市","type":"city","children":[]},{"code":"320500","name":"苏州市","type":"city","children":[]},{"code":"320600","name":"南通市","type":"city","children":[]},{"code":"320700","name":"连云港市","type":"city","children":[]},{"code":"320800","name":"淮安市","type":"city","children":[]},{"code":"320900","name":"盐城市","type":"city","children":[]},{"code":"321000","name":"扬州市","type":"city","children":[]},{"code":"321100","name":"镇江市","type":"city","children":[]},{"code":"321200","name":"泰州市","type":"city","children":[]},{"code":"321300","name":"宿迁市","type":"city","children":[]},{"code":"330100","name":"杭州市","type":"city","children":[]},{"code":"330200","name":"宁波市","type":"city","children":[]},{"code":"330300","name":"温州市","type":"city","children":[]},{"code":"330400","name":"嘉兴市","type":"city","children":[]},{"code":"330500","name":"湖州市","type":"city","children":[]},{"code":"330600","name":"绍兴市","type":"city","children":[]},{"code":"330700","name":"金华市","type":"city","children":[]},{"code":"330800","name":"衢州市","type":"city","children":[]},{"code":"330900","name":"舟山市","type":"city","children":[]},{"code":"331000","name":"台州市","type":"city","children":[]},{"code":"331100","name":"丽水市","type":"city","children":[]},{"code":"340100","name":"合肥市","type":"city","children":[]},{"code":"340200","name":"芜湖市","type":"city","children":[]},{"code":"340300","name":"蚌埠市","type":"city","children":[]},{"code":"340400","name":"淮南市","type":"city","children":[]},{"code":"340500","name":"马鞍山市","type":"city","children":[]},{"code":"340600","name":"淮北市","type":"city","children":[]},{"code":"340700","name":"铜陵市","type":"city","children":[]},{"code":"340800","name":"安庆市","type":"city","children":[]},{"code":"341000","name":"黄山市","type":"city","children":[]},{"code":"341100","name":"滁州市","type":"city","children":[]},{"code":"341200","name":"阜阳市","type":"city","children":[]},{"code":"341300","name":"宿州市","type":"city","children":[]},{"code":"341500","name":"六安市","type":"city","children":[]},{"code":"341600","name":"亳州市","type":"city","children":[]},{"code":"341700","name":"池州市","type":"city","children":[]},{"code":"341800","name":"宣城市","type":"city","children":[]},{"code":"350100","name":"福州市","type":"city","children":[]},{"code":"350200","name":"厦门市","type":"city","children":[]},{"code":"350300","name":"莆田市","type":"city","children":[]},{"code":"350400","name":"三明市","type":"city","children":[]},{"code":"350500","name":"泉州市","type":"city","children":[]},{"code":"350600","name":"漳州市","type":"city","children":[]},{"code":"350700","name":"南平市","type":"city","children":[]},{"code":"350800","name":"龙岩市","type":"city","children":[]},{"code":"350900","name":"宁德市","type":"city","children":[]},{"code":"360100","name":"南昌市","type":"city","children":[]},{"code":"360200","name":"景德镇市","type":"city","children":[]},{"code":"360300","name":"萍乡市","type":"city","children":[]},{"code":"360400","name":"九江市","type":"city","children":[]},{"code":"360500","name":"新余市","type":"city","children":[]},{"code":"360600","name":"鹰潭市","type":"city","children":[]},{"code":"360700","name":"赣州市","type":"city","children":[]},{"code":"360800","name":"吉安市","type":"city","children":[]},{"code":"360900","name":"宜春市","type":"city","children":[]},{"code":"361000","name":"抚州市","type":"city","children":[]},{"code":"361100","name":"上饶市","type":"city","children":[]},{"code":"370100","name":"济南市","type":"city","children":[]},{"code":"370200","name":"青岛市","type":"city","children":[]},{"code":"370300","name":"淄博市","type":"city","children":[]},{"code":"370400","name":"枣庄市","type":"city","children":[]},{"code":"370500","name":"东营市","type":"city","children":[]},{"code":"370600","name":"烟台市","type":"city","children":[]},{"code":"370700","name":"潍坊市","type":"city","children":[]},{"code":"370800","name":"济宁市","type":"city","children":[]},{"code":"370900","name":"泰安市","type":"city","children":[]},{"code":"371000","name":"威海市","type":"city","children":[]},{"code":"371100","name":"日照市","type":"city","children":[]},{"code":"371200","name":"莱芜市","type":"city","children":[]},{"code":"371300","name":"临沂市","type":"city","children":[]},{"code":"371400","name":"德州市","type":"city","children":[]},{"code":"371500","name":"聊城市","type":"city","children":[]},{"code":"371600","name":"滨州市","type":"city","children":[]},{"code":"371700","name":"菏泽市","type":"city","children":[]},{"code":"410100","name":"郑州市","type":"city","children":[]},{"code":"410200","name":"开封市","type":"city","children":[]},{"code":"410300","name":"洛阳市","type":"city","children":[]},{"code":"410400","name":"平顶山市","type":"city","children":[]},{"code":"410500","name":"安阳市","type":"city","children":[]},{"code":"410600","name":"鹤壁市","type":"city","children":[]},{"code":"410700","name":"新乡市","type":"city","children":[]},{"code":"410800","name":"焦作市","type":"city","children":[]},{"code":"410900","name":"濮阳市","type":"city","children":[]},{"code":"411000","name":"许昌市","type":"city","children":[]},{"code":"411100","name":"漯河市","type":"city","children":[]},{"code":"411200","name":"三门峡市","type":"city","children":[]},{"code":"411300","name":"南阳市","type":"city","children":[]},{"code":"411400","name":"商丘市","type":"city","children":[]},{"code":"411500","name":"信阳市","type":"city","children":[]},{"code":"411600","name":"周口市","type":"city","children":[]},{"code":"411700","name":"驻马店市","type":"city","children":[]},{"code":"419000","name":"省直辖县级行政区划","type":"city","children":[]},{"code":"420100","name":"武汉市","type":"city","children":[]},{"code":"420200","name":"黄石市","type":"city","children":[]},{"code":"420300","name":"十堰市","type":"city","children":[]},{"code":"420500","name":"宜昌市","type":"city","children":[]},{"code":"420600","name":"襄阳市","type":"city","children":[]},{"code":"420700","name":"鄂州市","type":"city","children":[]},{"code":"420800","name":"荆门市","type":"city","children":[]},{"code":"420900","name":"孝感市","type":"city","children":[]},{"code":"421000","name":"荆州市","type":"city","children":[]},{"code":"421100","name":"黄冈市","type":"city","children":[]},{"code":"421200","name":"咸宁市","type":"city","children":[]},{"code":"421300","name":"随州市","type":"city","children":[]},{"code":"422800","name":"恩施土家族苗族自治州","type":"city","children":[]},{"code":"429000","name":"省直辖县级行政区划","type":"city","children":[]},{"code":"430100","name":"长沙市","type":"city","children":[]},{"code":"430200","name":"株洲市","type":"city","children":[]},{"code":"430300","name":"湘潭市","type":"city","children":[]},{"code":"430400","name":"衡阳市","type":"city","children":[]},{"code":"430500","name":"邵阳市","type":"city","children":[]},{"code":"430600","name":"岳阳市","type":"city","children":[]},{"code":"430700","name":"常德市","type":"city","children":[]},{"code":"430800","name":"张家界市","type":"city","children":[]},{"code":"430900","name":"益阳市","type":"city","children":[]},{"code":"431000","name":"郴州市","type":"city","children":[]},{"code":"431100","name":"永州市","type":"city","children":[]},{"code":"431200","name":"怀化市","type":"city","children":[]},{"code":"431300","name":"娄底市","type":"city","children":[]},{"code":"433100","name":"湘西土家族苗族自治州","type":"city","children":[]},{"code":"440100","name":"广州市","type":"city","children":[]},{"code":"440200","name":"韶关市","type":"city","children":[]},{"code":"440300","name":"深圳市","type":"city","children":[]},{"code":"440400","name":"珠海市","type":"city","children":[]},{"code":"440500","name":"汕头市","type":"city","children":[]},{"code":"440600","name":"佛山市","type":"city","children":[]},{"code":"440700","name":"江门市","type":"city","children":[]},{"code":"440800","name":"湛江市","type":"city","children":[]},{"code":"440900","name":"茂名市","type":"city","children":[]},{"code":"441200","name":"肇庆市","type":"city","children":[]},{"code":"441300","name":"惠州市","type":"city","children":[]},{"code":"441400","name":"梅州市","type":"city","children":[]},{"code":"441500","name":"汕尾市","type":"city","children":[]},{"code":"441600","name":"河源市","type":"city","children":[]},{"code":"441700","name":"阳江市","type":"city","children":[]},{"code":"441800","name":"清远市","type":"city","children":[]},{"code":"441900","name":"东莞市","type":"city","children":[]},{"code":"442000","name":"中山市","type":"city","children":[]},{"code":"445100","name":"潮州市","type":"city","children":[]},{"code":"445200","name":"揭阳市","type":"city","children":[]},{"code":"445300","name":"云浮市","type":"city","children":[]},{"code":"450100","name":"南宁市","type":"city","children":[]},{"code":"450200","name":"柳州市","type":"city","children":[]},{"code":"450300","name":"桂林市","type":"city","children":[]},{"code":"450400","name":"梧州市","type":"city","children":[]},{"code":"450500","name":"北海市","type":"city","children":[]},{"code":"450600","name":"防城港市","type":"city","children":[]},{"code":"450700","name":"钦州市","type":"city","children":[]},{"code":"450800","name":"贵港市","type":"city","children":[]},{"code":"450900","name":"玉林市","type":"city","children":[]},{"code":"451000","name":"百色市","type":"city","children":[]},{"code":"451100","name":"贺州市","type":"city","children":[]},{"code":"451200","name":"河池市","type":"city","children":[]},{"code":"451300","name":"来宾市","type":"city","children":[]},{"code":"451400","name":"崇左市","type":"city","children":[]},{"code":"460100","name":"海口市","type":"city","children":[]},{"code":"460200","name":"三亚市","type":"city","children":[]},{"code":"460300","name":"三沙市","type":"city","children":[]},{"code":"460400","name":"儋州市","type":"city","children":[]},{"code":"469000","name":"省直辖县级行政区划","type":"city","children":[]},{"code":"500100","name":"市辖区","type":"city","children":[]},{"code":"500200","name":"县","type":"city","children":[]},{"code":"510100","name":"成都市","type":"city","children":[]},{"code":"510300","name":"自贡市","type":"city","children":[]},{"code":"510400","name":"攀枝花市","type":"city","children":[]},{"code":"510500","name":"泸州市","type":"city","children":[]},{"code":"510600","name":"德阳市","type":"city","children":[]},{"code":"510700","name":"绵阳市","type":"city","children":[]},{"code":"510800","name":"广元市","type":"city","children":[]},{"code":"510900","name":"遂宁市","type":"city","children":[]},{"code":"511000","name":"内江市","type":"city","children":[]},{"code":"511100","name":"乐山市","type":"city","children":[]},{"code":"511300","name":"南充市","type":"city","children":[]},{"code":"511400","name":"眉山市","type":"city","children":[]},{"code":"511500","name":"宜宾市","type":"city","children":[]},{"code":"511600","name":"广安市","type":"city","children":[]},{"code":"511700","name":"达州市","type":"city","children":[]},{"code":"511800","name":"雅安市","type":"city","children":[]},{"code":"511900","name":"巴中市","type":"city","children":[]},{"code":"512000","name":"资阳市","type":"city","children":[]},{"code":"513200","name":"阿坝藏族羌族自治州","type":"city","children":[]},{"code":"513300","name":"甘孜藏族自治州","type":"city","children":[]},{"code":"513400","name":"凉山彝族自治州","type":"city","children":[]},{"code":"520100","name":"贵阳市","type":"city","children":[]},{"code":"520200","name":"六盘水市","type":"city","children":[]},{"code":"520300","name":"遵义市","type":"city","children":[]},{"code":"520400","name":"安顺市","type":"city","children":[]},{"code":"520500","name":"毕节市","type":"city","children":[]},{"code":"520600","name":"铜仁市","type":"city","children":[]},{"code":"522300","name":"黔西南布依族苗族自治州","type":"city","children":[]},{"code":"522600","name":"黔东南苗族侗族自治州","type":"city","children":[]},{"code":"522700","name":"黔南布依族苗族自治州","type":"city","children":[]},{"code":"530100","name":"昆明市","type":"city","children":[]},{"code":"530300","name":"曲靖市","type":"city","children":[]},{"code":"530400","name":"玉溪市","type":"city","children":[]},{"code":"530500","name":"保山市","type":"city","children":[]},{"code":"530600","name":"昭通市","type":"city","children":[]},{"code":"530700","name":"丽江市","type":"city","children":[]},{"code":"530800","name":"普洱市","type":"city","children":[]},{"code":"530900","name":"临沧市","type":"city","children":[]},{"code":"532300","name":"楚雄彝族自治州","type":"city","children":[]},{"code":"532500","name":"红河哈尼族彝族自治州","type":"city","children":[]},{"code":"532600","name":"文山壮族苗族自治州","type":"city","children":[]},{"code":"532800","name":"西双版纳傣族自治州","type":"city","children":[]},{"code":"532900","name":"大理白族自治州","type":"city","children":[]},{"code":"533100","name":"德宏傣族景颇族自治州","type":"city","children":[]},{"code":"533300","name":"怒江傈僳族自治州","type":"city","children":[]},{"code":"533400","name":"迪庆藏族自治州","type":"city","children":[]},{"code":"540100","name":"拉萨市","type":"city","children":[]},{"code":"540200","name":"日喀则市","type":"city","children":[]},{"code":"540300","name":"昌都市","type":"city","children":[]},{"code":"540400","name":"林芝市","type":"city","children":[]},{"code":"540500","name":"山南市","type":"city","children":[]},{"code":"542400","name":"那曲地区","type":"city","children":[]},{"code":"542500","name":"阿里地区","type":"city","children":[]},{"code":"610100","name":"西安市","type":"city","children":[]},{"code":"610200","name":"铜川市","type":"city","children":[]},{"code":"610300","name":"宝鸡市","type":"city","children":[]},{"code":"610400","name":"咸阳市","type":"city","children":[]},{"code":"610500","name":"渭南市","type":"city","children":[]},{"code":"610600","name":"延安市","type":"city","children":[]},{"code":"610700","name":"汉中市","type":"city","children":[]},{"code":"610800","name":"榆林市","type":"city","children":[]},{"code":"610900","name":"安康市","type":"city","children":[]},{"code":"611000","name":"商洛市","type":"city","children":[]},{"code":"620100","name":"兰州市","type":"city","children":[]},{"code":"620200","name":"嘉峪关市","type":"city","children":[]},{"code":"620300","name":"金昌市","type":"city","children":[]},{"code":"620400","name":"白银市","type":"city","children":[]},{"code":"620500","name":"天水市","type":"city","children":[]},{"code":"620600","name":"武威市","type":"city","children":[]},{"code":"620700","name":"张掖市","type":"city","children":[]},{"code":"620800","name":"平凉市","type":"city","children":[]},{"code":"620900","name":"酒泉市","type":"city","children":[]},{"code":"621000","name":"庆阳市","type":"city","children":[]},{"code":"621100","name":"定西市","type":"city","children":[]},{"code":"621200","name":"陇南市","type":"city","children":[]},{"code":"622900","name":"临夏回族自治州","type":"city","children":[]},{"code":"623000","name":"甘南藏族自治州","type":"city","children":[]},{"code":"630100","name":"西宁市","type":"city","children":[]},{"code":"630200","name":"海东市","type":"city","children":[]},{"code":"632200","name":"海北藏族自治州","type":"city","children":[]},{"code":"632300","name":"黄南藏族自治州","type":"city","children":[]},{"code":"632500","name":"海南藏族自治州","type":"city","children":[]},{"code":"632600","name":"果洛藏族自治州","type":"city","children":[]},{"code":"632700","name":"玉树藏族自治州","type":"city","children":[]},{"code":"632800","name":"海西蒙古族藏族自治州","type":"city","children":[]},{"code":"640100","name":"银川市","type":"city","children":[]},{"code":"640200","name":"石嘴山市","type":"city","children":[]},{"code":"640300","name":"吴忠市","type":"city","children":[]},{"code":"640400","name":"固原市","type":"city","children":[]},{"code":"640500","name":"中卫市","type":"city","children":[]},{"code":"650100","name":"乌鲁木齐市","type":"city","children":[]},{"code":"650200","name":"克拉玛依市","type":"city","children":[]},{"code":"650400","name":"吐鲁番市","type":"city","children":[]},{"code":"650500","name":"哈密市","type":"city","children":[]},{"code":"652300","name":"昌吉回族自治州","type":"city","children":[]},{"code":"652700","name":"博尔塔拉蒙古自治州","type":"city","children":[]},{"code":"652800","name":"巴音郭楞蒙古自治州","type":"city","children":[]},{"code":"652900","name":"阿克苏地区","type":"city","children":[]},{"code":"653000","name":"克孜勒苏柯尔克孜自治州","type":"city","children":[]},{"code":"653100","name":"喀什地区","type":"city","children":[]},{"code":"653200","name":"和田地区","type":"city","children":[]},{"code":"654000","name":"伊犁哈萨克自治州","type":"city","children":[]},{"code":"654200","name":"塔城地区","type":"city","children":[]},{"code":"654300","name":"阿勒泰地区","type":"city","children":[]},{"code":"659000","name":"自治区直辖县级行政区划","type":"city","children":[]}] --------------------------------------------------------------------------------